chore: merge release-v0.2.4 into main

This commit is contained in:
Jack Wills
2023-03-02 05:37:53 +00:00
19 changed files with 615 additions and 335 deletions
+1 -1
View File
@@ -17,7 +17,7 @@
"seccomp=unconfined" "seccomp=unconfined"
], ],
"postCreateCommand": "cargo install cross typos-cli", "postCreateCommand": "rustup target add x86_64-unknown-linux-musl && cargo install cross typos-cli",
"mounts": [ "mounts": [
"source=/etc/timezone,target=/etc/timezone,type=bind,readonly" "source=/etc/timezone,target=/etc/timezone,type=bind,readonly"
+22 -2
View File
@@ -1,7 +1,27 @@
### 2023-02-04 ### 2023-03-02
### Chores
+ dependencies updated, [aac3ef2b1def3345d749d813d9b76020d6b5e5ca], [4723be7fb2eb101024bb9d5a514e2c6cc51eb6f6], [c69ab4f7c3b873f25ea46958add37be78d23e9cf], [ba6437862dae0f422660a602aeabd6217d023fac], [2bb4c338903e09856053894d9646307e31d32f1c]
+ dev container install x86 musl toolchain, [e650034d50f01a7598876d4f2887df691700e06a]
### Docs
+ typos removed, [23ad9a5fb3cacf3fb8cb70c65ca9133ed9949e45], [cebb975cb82f653407ec801fd8c726ca6ed68289], [fdc67c9249a239bac97a78b20c9378472865209c]
+ comments improved, [ec962295a8789ff8010604e974969bf618ea7108]
### Features
+ mouse capture is now more specific, should have substantial performance impact, 10x reduction in cpu usage when mouse is moved observed, as well as fixing intermittent mouse events output bug, [0a1b53111627206cc7436589e5b7212e1b72edb8], [93f7c07f708885f8870da5dfb6d57c62f93c9c78], [c74f6c1179b5f62989eb74f395a56b43a8781b03]
+ improve the styling of the help information popup, [28de74b866f07c8543e46be3cab929eff28953fd]
+ use checked_sub & checked_div for bounds checks, [72279e26ae996353c95a75527f704bac1e4bcf4d]
### Fixes ### Fixes
+ Container runner `FROM scratch` (missing from v0.2.2 D'oh), this now should actually reduce Docker image size by ~60%, [0bd317b7ce6f9f42a614c488099b5fc7a14d91c7] + correctly set gui error, [340893a860e99ec4029d12613f2a6de3cb7b47e2]
### Refactors
+ dead code removed, [b8f5792d1865d3a398cd7f23aa9473a55dc6ea44]
+ improve the get_width function, [04c26fe8fc7c79506921b9cff42825b1ee132737]
+ place ui methods into a Ui struct, [3437df59884f084624031fceb34ea3012a8e2251]
+ get_horizotal/vertical constraints into single method, [e8f5cf9c6f8cd5f807a05fb61e31d7cd1426486f]
+ docker update_everything variables, [074cb957f274675a468f08fecb1c43ff7453217d]
see <a href='https://github.com/mrjackwills/oxker/blob/main/CHANGELOG.md'>CHANGELOG.md</a> for more details see <a href='https://github.com/mrjackwills/oxker/blob/main/CHANGELOG.md'>CHANGELOG.md</a> for more details
@@ -38,6 +38,7 @@ jobs:
run: cargo install cross --git https://github.com/cross-rs/cross run: cargo install cross --git https://github.com/cross-rs/cross
# Build for linux x86_64 # Build for linux x86_64
# Should this actually build for musl?
- name: build release linux_x86_64 - name: build release linux_x86_64
run: cargo build --release run: cargo build --release
# Compress output into tar # Compress output into tar
+26
View File
@@ -1,3 +1,29 @@
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.2.4'>v0.2.4</a>
### 2023-03-02
### Chores
+ dependencies updated, [aac3ef2b](https://github.com/mrjackwills/oxker/commit/aac3ef2b1def3345d749d813d9b76020d6b5e5ca), [4723be7f](https://github.com/mrjackwills/oxker/commit/4723be7fb2eb101024bb9d5a514e2c6cc51eb6f6), [c69ab4f7](https://github.com/mrjackwills/oxker/commit/c69ab4f7c3b873f25ea46958add37be78d23e9cf), [ba643786](https://github.com/mrjackwills/oxker/commit/ba6437862dae0f422660a602aeabd6217d023fac), [2bb4c338](https://github.com/mrjackwills/oxker/commit/2bb4c338903e09856053894d9646307e31d32f1c)
+ dev container install x86 musl toolchain, [e650034d](https://github.com/mrjackwills/oxker/commit/e650034d50f01a7598876d4f2887df691700e06a)
### Docs
+ typos removed, [23ad9a5f](https://github.com/mrjackwills/oxker/commit/23ad9a5fb3cacf3fb8cb70c65ca9133ed9949e45), [cebb975c](https://github.com/mrjackwills/oxker/commit/cebb975cb82f653407ec801fd8c726ca6ed68289), [fdc67c92](https://github.com/mrjackwills/oxker/commit/fdc67c9249a239bac97a78b20c9378472865209c)
+ comments improved, [ec962295](https://github.com/mrjackwills/oxker/commit/ec962295a8789ff8010604e974969bf618ea7108)
### Features
+ mouse capture is now more specific, should have substantial performance impact, 10x reduction in cpu usage when mouse is moved observed, as well as fixing intermittent mouse events output bug, [0a1b5311](https://github.com/mrjackwills/oxker/commit/0a1b53111627206cc7436589e5b7212e1b72edb8), [93f7c07f](https://github.com/mrjackwills/oxker/commit/93f7c07f708885f8870da5dfb6d57c62f93c9c78), [c74f6c11](https://github.com/mrjackwills/oxker/commit/c74f6c1179b5f62989eb74f395a56b43a8781b03)
+ improve the styling of the help information popup, [28de74b8](https://github.com/mrjackwills/oxker/commit/28de74b866f07c8543e46be3cab929eff28953fd)
+ use checked_sub & checked_div for bounds checks, [72279e26](https://github.com/mrjackwills/oxker/commit/72279e26ae996353c95a75527f704bac1e4bcf4d)
### Fixes
+ correctly set gui error, [340893a8](https://github.com/mrjackwills/oxker/commit/340893a860e99ec4029d12613f2a6de3cb7b47e2)
### Refactors
+ dead code removed, [b8f5792d](https://github.com/mrjackwills/oxker/commit/b8f5792d1865d3a398cd7f23aa9473a55dc6ea44)
+ improve the get_width function, [04c26fe8](https://github.com/mrjackwills/oxker/commit/04c26fe8fc7c79506921b9cff42825b1ee132737)
+ place ui methods into a Ui struct, [3437df59](https://github.com/mrjackwills/oxker/commit/3437df59884f084624031fceb34ea3012a8e2251)
+ get_horizotal/vertical constraints into single method, [e8f5cf9c](https://github.com/mrjackwills/oxker/commit/e8f5cf9c6f8cd5f807a05fb61e31d7cd1426486f)
+ docker update_everything variables, [074cb957](https://github.com/mrjackwills/oxker/commit/074cb957f274675a468f08fecb1c43ff7453217d)
# <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.2.3'>v0.2.3</a> # <a href='https://github.com/mrjackwills/oxker/releases/tag/v0.2.3'>v0.2.3</a>
### 2023-02-04 ### 2023-02-04
+1 -1
View File
@@ -4,7 +4,7 @@ oxker encourages any, and all, suggestions, bug reports, pull requests, and/or f
## Submitting Issues ## Submitting Issues
Please use the oxker [issue templates for](https://github.com/mrjackwills/oxker/issues/new/choose) any Bug Report, Feature Suggestions, Please use the oxker [issue templates](https://github.com/mrjackwills/oxker/issues/new/choose) for any Bug Report, Feature Suggestions,
Refactor Idea, or Security Vulnerabilities. Refactor Idea, or Security Vulnerabilities.
Don't hesitate to submit any issues or pull requests, regardless of size. Don't hesitate to submit any issues or pull requests, regardless of size.
Generated
+62 -76
View File
@@ -13,9 +13,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.68" version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
@@ -131,9 +131,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.1.4" version = "4.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"clap_derive", "clap_derive",
@@ -148,9 +148,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.1.0" version = "4.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro-error", "proc-macro-error",
@@ -161,9 +161,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.3.1" version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
dependencies = [ dependencies = [
"os_str_bytes", "os_str_bytes",
] ]
@@ -202,9 +202,9 @@ dependencies = [
[[package]] [[package]]
name = "crossterm" name = "crossterm"
version = "0.26.0" version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77f67c7faacd4db07a939f55d66a983a5355358a1f17d32cc9a8d01d1266b9ce" checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crossterm_winapi", "crossterm_winapi",
@@ -227,9 +227,9 @@ dependencies = [
[[package]] [[package]]
name = "cxx" name = "cxx"
version = "1.0.89" version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62"
dependencies = [ dependencies = [
"cc", "cc",
"cxxbridge-flags", "cxxbridge-flags",
@@ -239,9 +239,9 @@ dependencies = [
[[package]] [[package]]
name = "cxx-build" name = "cxx-build"
version = "1.0.89" version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690"
dependencies = [ dependencies = [
"cc", "cc",
"codespan-reporting", "codespan-reporting",
@@ -254,15 +254,15 @@ dependencies = [
[[package]] [[package]]
name = "cxxbridge-flags" name = "cxxbridge-flags"
version = "1.0.89" version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf"
[[package]] [[package]]
name = "cxxbridge-macro" name = "cxxbridge-macro"
version = "1.0.89" version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -370,9 +370,9 @@ dependencies = [
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.3.15" version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@@ -410,9 +410,9 @@ dependencies = [
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.3.0" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]] [[package]]
name = "hex" name = "hex"
@@ -422,9 +422,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.8" version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@@ -543,19 +543,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.45.0", "windows-sys",
] ]
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.3" version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857"
dependencies = [ dependencies = [
"hermit-abi 0.3.0", "hermit-abi 0.3.1",
"io-lifetimes", "io-lifetimes",
"rustix", "rustix",
"windows-sys 0.45.0", "windows-sys",
] ]
[[package]] [[package]]
@@ -627,14 +627,14 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.5" version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
"wasi", "wasi",
"windows-sys 0.42.0", "windows-sys",
] ]
[[package]] [[package]]
@@ -678,9 +678,9 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.17.0" version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]] [[package]]
name = "os_str_bytes" name = "os_str_bytes"
@@ -696,13 +696,13 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]] [[package]]
name = "oxker" name = "oxker"
version = "0.2.3" version = "0.2.4"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bollard", "bollard",
"cansi", "cansi",
"clap", "clap",
"crossterm 0.26.0", "crossterm 0.26.1",
"futures-util", "futures-util",
"parking_lot", "parking_lot",
"tokio", "tokio",
@@ -732,7 +732,7 @@ dependencies = [
"libc", "libc",
"redox_syscall", "redox_syscall",
"smallvec", "smallvec",
"windows-sys 0.45.0", "windows-sys",
] ]
[[package]] [[package]]
@@ -805,9 +805,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.50" version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -871,7 +871,7 @@ dependencies = [
"io-lifetimes", "io-lifetimes",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.45.0", "windows-sys",
] ]
[[package]] [[package]]
@@ -914,9 +914,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.91" version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@@ -972,9 +972,9 @@ dependencies = [
[[package]] [[package]]
name = "signal-hook" name = "signal-hook"
version = "0.3.14" version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
dependencies = [ dependencies = [
"libc", "libc",
"signal-hook-registry", "signal-hook-registry",
@@ -993,18 +993,18 @@ dependencies = [
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.0" version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [ dependencies = [
"libc", "libc",
] ]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.7" version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
dependencies = [ dependencies = [
"autocfg", "autocfg",
] ]
@@ -1033,9 +1033,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.107" version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1073,18 +1073,19 @@ dependencies = [
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.1.4" version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
dependencies = [ dependencies = [
"cfg-if",
"once_cell", "once_cell",
] ]
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.17" version = "0.3.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
dependencies = [ dependencies = [
"itoa", "itoa",
"serde", "serde",
@@ -1100,9 +1101,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.2.6" version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36"
dependencies = [ dependencies = [
"time-core", "time-core",
] ]
@@ -1124,9 +1125,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.25.0" version = "1.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"bytes", "bytes",
@@ -1139,7 +1140,7 @@ dependencies = [
"signal-hook-registry", "signal-hook-registry",
"socket2", "socket2",
"tokio-macros", "tokio-macros",
"windows-sys 0.42.0", "windows-sys",
] ]
[[package]] [[package]]
@@ -1155,9 +1156,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.4" version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
@@ -1426,21 +1427,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.45.0" version = "0.45.0"
+2 -2
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "oxker" name = "oxker"
version = "0.2.3" version = "0.2.4"
edition = "2021" edition = "2021"
authors = ["Jack Wills <email@mrjackwills.com>"] authors = ["Jack Wills <email@mrjackwills.com>"]
description = "A simple tui to view & control docker containers" description = "A simple tui to view & control docker containers"
@@ -19,7 +19,7 @@ clap={version="4.1", features = ["derive", "unicode", "color"] }
crossterm = "0.26" crossterm = "0.26"
futures-util = "0.3" futures-util = "0.3"
parking_lot = {version= "0.12"} parking_lot = {version= "0.12"}
tokio = {version = "1.25", features=["full"]} tokio = {version = "1.26", features=["full"]}
tracing = "0.1" tracing = "0.1"
tracing-subscriber = "0.3" tracing-subscriber = "0.3"
tui = "0.19" tui = "0.19"
+45 -16
View File
@@ -21,34 +21,45 @@
### Cargo ### Cargo
Published on <a href='https://www.crates.io/crates/oxker' target='_blank' rel='noopener noreferrer'>crates.io</a>, so if you have cargo installed, simply run Published on <a href='https://www.crates.io/crates/oxker' target='_blank' rel='noopener noreferrer'>crates.io</a>, so if you have cargo installed, simply run
```cargo install oxker```
```shell
cargo install oxker
```
### Docker ### Docker
Published on <a href='https://hub.docker.com/r/mrjackwills/oxker' target='_blank' rel='noopener noreferrer'>Docker Hub</a>, with images built for `linux/amd64`, `linux/arm64`, and `linux/arm/v6` Published on <a href='https://hub.docker.com/r/mrjackwills/oxker' target='_blank' rel='noopener noreferrer'>Docker Hub</a>, with images built for `linux/amd64`, `linux/arm64`, and `linux/arm/v6`
`docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock:ro --pull=always mrjackwills/oxker` ```shell
docker run --rm -it -v /var/run/docker.sock:/var/run/docker.sock:ro --pull=always mrjackwills/oxker
```
### Nix ### Nix
Using nix flakes, oxker can be ran directly with Using nix flakes, oxker can be ran directly with
```nix run nixpkgs#oxker``` ```shell
nix run nixpkgs#oxker
```
Without flakes, you can build a shell that contains oxker using Without flakes, you can build a shell that contains oxker using
```nix-shell -p oxker``` ```shell
nix-shell -p oxker
```
### AUR ### AUR
oxker can be installed from the [AUR](https://aur.archlinux.org/packages/oxker) with using an [AUR helper](https://wiki.archlinux.org/title/AUR_helpers): oxker can be installed from the [AUR](https://aur.archlinux.org/packages/oxker) with using an [AUR helper](https://wiki.archlinux.org/title/AUR_helpers):
```paru -S oxker``` ```shell
paru -S oxker
```
### Pre-Built ### Pre-Built
See the <a href="https://github.com/mrjackwills/oxker/releases/latest" target='_blank' rel='noopener noreferrer'>pre-built binaries</a> See the <a href="https://github.com/mrjackwills/oxker/releases/latest" target='_blank' rel='noopener noreferrer'>pre-built binaries</a>
or, download & install (x86_64 one liner) or, download & install (x86_64 one liner)
```bash ```shell
wget https://www.github.com/mrjackwills/oxker/releases/latest/download/oxker_linux_x86_64.tar.gz && wget https://www.github.com/mrjackwills/oxker/releases/latest/download/oxker_linux_x86_64.tar.gz &&
tar xzvf oxker_linux_x86_64.tar.gz oxker && tar xzvf oxker_linux_x86_64.tar.gz oxker &&
install -Dm 755 oxker -t "${HOME}/.local/bin" && install -Dm 755 oxker -t "${HOME}/.local/bin" &&
@@ -59,13 +70,15 @@ or, for automatic platform selection, download, and installation (to `$HOME/.loc
*One should always verify script content before running in a shell* *One should always verify script content before running in a shell*
```bash ```shell
curl https://raw.githubusercontent.com/mrjackwills/oxker/main/install.sh | bash curl https://raw.githubusercontent.com/mrjackwills/oxker/main/install.sh | bash
``` ```
## Run ## Run
```oxker``` ```shell
oxker
```
In application controls In application controls
| button| result| | button| result|
@@ -94,7 +107,9 @@ Available command line arguments
### x86_64 ### x86_64
```cargo build --release``` ```shell
cargo build --release
```
### Raspberry pi ### Raspberry pi
@@ -102,13 +117,17 @@ requires docker & <a href='https://github.com/cross-rs/cross' target='_blank' re
#### 64bit pi (pi 4, pi zero w 2) #### 64bit pi (pi 4, pi zero w 2)
```cross build --target aarch64-unknown-linux-gnu --release``` ```shell
cross build --target aarch64-unknown-linux-gnu --release
```
#### 32bit pi (pi zero w) #### 32bit pi (pi zero w)
Tested, and fully working on pi zero w, running Raspberry Pi OS 32 bit, the initial logs parsing can take an extended period of time if thousands of lines long, suggest running with a -d argument of 5000 Tested, and fully working on pi zero w, running Raspberry Pi OS 32 bit, the initial logs parsing can take an extended period of time if thousands of lines long, suggest running with a -d argument of 5000
```cross build --target arm-unknown-linux-musleabihf --release``` ```shell
cross build --target arm-unknown-linux-musleabihf --release
```
If no memory information available, try appending ```/boot/cmdline.txt``` with If no memory information available, try appending ```/boot/cmdline.txt``` with
@@ -122,18 +141,28 @@ see <a href="https://forums.raspberrypi.com/viewtopic.php?t=203128" target='_bla
As of yet untested, needs work As of yet untested, needs work
```cargo test -- --test-threads=1``` ```shell
cargo test -- --test-threads=1
```
Run some example docker images Run some example docker images
using docker-compose.yml; using docker-compose.yml;
```docker compose -f docker-compose.yml up -d``` ```shell
docker compose -f docker-compose.yml up -d
```
or individually or individually
```docker run --name redis -d redis:alpine3.17``` ```shell
docker run --name redis -d redis:alpine3.17
```
```docker run --name postgres -e POSTGRES_PASSWORD=never_use_this_password_in_production -d postgres:alpine3.17``` ```shell
docker run --name postgres -e POSTGRES_PASSWORD=never_use_this_password_in_production -d postgres:alpine3.17
```
```docker run -d --hostname my-rabbit --name rabbitmq rabbitmq:3``` ```shell
docker run -d --hostname my-rabbit --name rabbitmq rabbitmq:3
```
+2 -2
View File
@@ -58,5 +58,5 @@ ENV OXKER_RUNTIME=container
COPY --from=BUILDER /oxker /app/ COPY --from=BUILDER /oxker /app/
# Run the application # Run the application
# this is used in the application itself, to stop itself show when running from a docker container, so DO NOT EDIT # this is used in the application itself, to stop itself from listing itself, so DO NOT EDIT
ENTRYPOINT [ "./app/oxker"] ENTRYPOINT [ "/app/oxker"]
+2 -2
View File
@@ -11,10 +11,10 @@ COPY ./target/x86_64-unknown-linux-musl/release/oxker /app/
# Run the application # Run the application
# this is used in the application itself, to stop itself show when running from a docker container, so DO NOT EDIT # this is used in the application itself, to stop itself show when running from a docker container, so DO NOT EDIT
ENTRYPOINT [ "./app/oxker"] ENTRYPOINT [ "/app/oxker"]
# Dev build for testing # Dev build for testing
# docker build -t oxker_dev -f Dockerfile . && docker run --rm -it --volume /var/run/docker.sock:/var/run/docker.sock:ro oxker_dev # docker build -t oxker_dev -f containerised/Dockerfile_dev . && docker run --rm -it --volume /var/run/docker.sock:/var/run/docker.sock:ro oxker_dev
# Dev build one liner, x86 host # Dev build one liner, x86 host
# docker image prune -a; cargo build --release --target x86_64-unknown-linux-musl && docker build -t oxker_dev -f containerised/Dockerfile_dev . && docker run --rm -it --volume /var/run/docker.sock:/var/run/docker.sock:ro oxker_dev # docker image prune -a; cargo build --release --target x86_64-unknown-linux-musl && docker build -t oxker_dev -f containerised/Dockerfile_dev . && docker run --rm -it --volume /var/run/docker.sock:/var/run/docker.sock:ro oxker_dev
+3 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# rust create_release # rust create_release
# v0.2.1 # v0.2.2
STAR_LINE='****************************************' STAR_LINE='****************************************'
CWD=$(pwd) CWD=$(pwd)
@@ -231,6 +231,8 @@ release_flow() {
echo -e "\ncargo fmt" echo -e "\ncargo fmt"
cargo fmt cargo fmt
echo -e "\n${PURPLE}cargo check${RESET}\n"
cargo check
release_continue "git add ." release_continue "git add ."
git add . git add .
+1 -1
View File
@@ -142,7 +142,7 @@ impl State {
_ => Color::Red, _ => Color::Red,
} }
} }
// Dirty way to create order for the state, rather than impl Ord /// Dirty way to create order for the state, rather than impl Ord
pub const fn order(self) -> u8 { pub const fn order(self) -> u8 {
match self { match self {
Self::Running => 0, Self::Running => 0,
+11 -38
View File
@@ -203,7 +203,7 @@ impl AppData {
self.containers.start(); self.containers.start();
} }
// select the last container /// select the last container
pub fn containers_end(&mut self) { pub fn containers_end(&mut self) {
self.containers.end(); self.containers.end();
} }
@@ -213,7 +213,7 @@ impl AppData {
self.containers.next(); self.containers.next();
} }
// select the previous container /// select the previous container
pub fn containers_previous(&mut self) { pub fn containers_previous(&mut self) {
self.containers.previous(); self.containers.previous();
} }
@@ -428,12 +428,6 @@ impl AppData {
.to_string(), .to_string(),
); );
let rx_count = count(&container.rx.to_string());
let tx_count = count(&container.tx.to_string());
let image_count = count(&container.image);
let name_count = count(&container.name);
let state_count = count(&container.state.to_string());
let status_count = count(&container.status);
let mem_current_count = count( let mem_current_count = count(
&container &container
.mem_stats .mem_stats
@@ -441,35 +435,16 @@ impl AppData {
.unwrap_or(&ByteStats::default()) .unwrap_or(&ByteStats::default())
.to_string(), .to_string(),
); );
let mem_limit_count = count(&container.mem_limit.to_string());
if cpu_count > columns.cpu.1 { columns.cpu.1 = columns.cpu.1.max(cpu_count);
columns.cpu.1 = cpu_count; columns.image.1 = columns.image.1.max(count(&container.image));
}; columns.mem.1 = columns.mem.1.max(mem_current_count);
if image_count > columns.image.1 { columns.mem.2 = columns.mem.2.max(count(&container.mem_limit.to_string()));
columns.image.1 = image_count; columns.name.1 = columns.name.1.max(count(&container.name));
}; columns.net_rx.1 = columns.net_rx.1.max(count(&container.rx.to_string()));
if mem_current_count > columns.mem.1 { columns.net_tx.1 = columns.net_tx.1.max(count(&container.tx.to_string()));
columns.mem.1 = mem_current_count; columns.state.1 = columns.state.1.max(count(&container.state.to_string()));
}; columns.status.1 = columns.status.1.max(count(&container.status));
if mem_limit_count > columns.mem.2 {
columns.mem.2 = mem_limit_count;
};
if name_count > columns.name.1 {
columns.name.1 = name_count;
};
if state_count > columns.state.1 {
columns.state.1 = state_count;
};
if status_count > columns.status.1 {
columns.status.1 = status_count;
};
if rx_count > columns.net_rx.1 {
columns.net_rx.1 = rx_count;
};
if tx_count > columns.net_tx.1 {
columns.net_tx.1 = tx_count;
};
} }
columns columns
} }
@@ -519,9 +494,7 @@ impl AppData {
container.mem_limit.update(mem_limit); container.mem_limit.update(mem_limit);
} }
// need to benchmark this? // need to benchmark this?
// if self.get_sorted().is_some() {
self.sort_containers(); self.sort_containers();
// }
} }
/// Update, or insert, containers /// Update, or insert, containers
+8 -11
View File
@@ -7,10 +7,7 @@ use futures_util::StreamExt;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{ use std::{
collections::HashMap, collections::HashMap,
sync::{ sync::{atomic::AtomicBool, Arc},
atomic::{AtomicBool, Ordering},
Arc,
},
}; };
use tokio::{sync::mpsc::Receiver, task::JoinHandle}; use tokio::{sync::mpsc::Receiver, task::JoinHandle};
use uuid::Uuid; use uuid::Uuid;
@@ -34,7 +31,7 @@ enum SpawnId {
/// Cpu & Mem stats take twice as long as the update interval to get a value, so will have two being executed at the same time /// Cpu & Mem stats take twice as long as the update interval to get a value, so will have two being executed at the same time
/// SpawnId::Stats takes container_id and binate value to enable both cycles of the same container_id to be inserted into the hashmap /// SpawnId::Stats takes container_id and binate value to enable both cycles of the same container_id to be inserted into the hashmap
/// Binate value is toggled when all handles have been spawned off /// Binate value is toggled when all handles have been spawned off
/// Also effectively means that if the docker_update interval minimum will be 1000ms /// Also effectively means that the docker_update interval minimum will be 1000ms
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] #[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
enum Binate { enum Binate {
One, One,
@@ -176,7 +173,7 @@ impl DockerData {
/// Get all current containers, handle into ContainerItem in the app_data struct rather than here /// Get all current containers, handle into ContainerItem in the app_data struct rather than here
/// Just make sure that items sent are guaranteed to have an id /// Just make sure that items sent are guaranteed to have an id
/// If in a containerised runtime, will ignore any container that uses the q`./app/oxker` as an entry point, unless the `-s` flag is set /// If in a containerised runtime, will ignore any container that uses the `/app/oxker` as an entry point, unless the `-s` flag is set
pub async fn update_all_containers(&mut self) -> Vec<(bool, ContainerId)> { pub async fn update_all_containers(&mut self) -> Vec<(bool, ContainerId)> {
let containers = self let containers = self
.docker .docker
@@ -270,14 +267,14 @@ impl DockerData {
async fn update_everything(&mut self) { async fn update_everything(&mut self) {
let all_ids = self.update_all_containers().await; let all_ids = self.update_all_containers().await;
if let Some(container) = self.app_data.lock().get_selected_container() { if let Some(container) = self.app_data.lock().get_selected_container() {
let id = container.id.clone();
let last_updated = container.last_updated; let last_updated = container.last_updated;
self.spawns self.spawns
.lock() .lock()
.entry(SpawnId::Log(id.clone())) .entry(SpawnId::Log(container.id.clone()))
.or_insert_with(|| { .or_insert_with(|| {
let docker = Arc::clone(&self.docker);
let app_data = Arc::clone(&self.app_data); let app_data = Arc::clone(&self.app_data);
let docker = Arc::clone(&self.docker);
let id = container.id.clone();
let spawns = Arc::clone(&self.spawns); let spawns = Arc::clone(&self.spawns);
tokio::spawn(Self::update_log(app_data, docker, id, last_updated, spawns)) tokio::spawn(Self::update_log(app_data, docker, id, last_updated, spawns))
}); });
@@ -407,7 +404,8 @@ impl DockerData {
.values() .values()
.into_iter() .into_iter()
.for_each(tokio::task::JoinHandle::abort); .for_each(tokio::task::JoinHandle::abort);
self.is_running.store(false, Ordering::SeqCst); self.is_running
.store(false, std::sync::atomic::Ordering::SeqCst);
} }
} }
} }
@@ -436,7 +434,6 @@ impl DockerData {
spawns: Arc::new(Mutex::new(HashMap::new())), spawns: Arc::new(Mutex::new(HashMap::new())),
}; };
inner.initialise_container_data().await; inner.initialise_container_data().await;
inner.message_handler().await; inner.message_handler().await;
} }
} }
+14 -19
View File
@@ -4,9 +4,7 @@ use std::sync::{
}; };
use crossterm::{ use crossterm::{
event::{ event::{DisableMouseCapture, KeyCode, MouseButton, MouseEvent, MouseEventKind},
DisableMouseCapture, EnableMouseCapture, KeyCode, MouseButton, MouseEvent, MouseEventKind,
},
execute, execute,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
@@ -21,7 +19,7 @@ use crate::{
app_data::{AppData, DockerControls, Header}, app_data::{AppData, DockerControls, Header},
app_error::AppError, app_error::AppError,
docker_data::DockerMessage, docker_data::DockerMessage,
ui::{GuiState, SelectablePanel, Status}, ui::{GuiState, SelectablePanel, Status, Ui},
}; };
pub use message::InputMessages; pub use message::InputMessages;
@@ -82,27 +80,23 @@ impl InputHandler {
/// Toggle the mouse capture (via input of the 'm' key) /// Toggle the mouse capture (via input of the 'm' key)
fn m_key(&mut self) { fn m_key(&mut self) {
if self.mouse_capture { if self.mouse_capture {
match execute!(std::io::stdout(), DisableMouseCapture) { if execute!(std::io::stdout(), DisableMouseCapture).is_ok() {
Ok(_) => self self.gui_state
.gui_state
.lock() .lock()
.set_info_box("✖ mouse capture disabled".to_owned()), .set_info_box("✖ mouse capture disabled".to_owned());
Err(_) => { } else {
self.app_data self.app_data
.lock() .lock()
.set_error(AppError::MouseCapture(false)); .set_error(AppError::MouseCapture(false));
self.gui_state.lock().status_push(Status::Error);
} }
} } else if Ui::enable_mouse_capture().is_ok() {
} else { self.gui_state
match execute!(std::io::stdout(), EnableMouseCapture) {
Ok(_) => self
.gui_state
.lock() .lock()
.set_info_box("✓ mouse capture enabled".to_owned()), .set_info_box("✓ mouse capture enabled".to_owned());
Err(_) => { } else {
self.app_data.lock().set_error(AppError::MouseCapture(true)); self.app_data.lock().set_error(AppError::MouseCapture(true));
} self.gui_state.lock().status_push(Status::Error);
}
}; };
// If the info box sleep handle is currently being executed, as in 'm' is pressed twice within a 4000ms window // If the info box sleep handle is currently being executed, as in 'm' is pressed twice within a 4000ms window
@@ -134,7 +128,8 @@ impl InputHandler {
.lock() .lock()
.status_contains(&[Status::Error, Status::Init]); .status_contains(&[Status::Error, Status::Init]);
if error_init || self.docker_sender.send(DockerMessage::Quit).await.is_err() { if error_init || self.docker_sender.send(DockerMessage::Quit).await.is_err() {
self.is_running.store(false, Ordering::SeqCst); self.is_running
.store(false, std::sync::atomic::Ordering::SeqCst);
} }
} }
+5 -7
View File
@@ -1,13 +1,13 @@
#![forbid(unsafe_code)] #![forbid(unsafe_code)]
#![warn( #![warn(
clippy::nursery,
clippy::pedantic,
clippy::expect_used, clippy::expect_used,
clippy::todo, clippy::todo,
clippy::unused_async, clippy::unused_async,
clippy::unwrap_used clippy::unwrap_used
)] )]
// Warning - These are indeed pedantic // Warning - These are indeed pedantic
#![warn(clippy::pedantic)]
#![warn(clippy::nursery)]
#![allow( #![allow(
clippy::module_name_repetitions, clippy::module_name_repetitions,
clippy::doc_markdown, clippy::doc_markdown,
@@ -34,12 +34,12 @@ mod input_handler;
mod parse_args; mod parse_args;
mod ui; mod ui;
use ui::{create_ui, GuiState, Status}; use ui::{GuiState, Status, Ui};
use crate::docker_data::DockerMessage; use crate::docker_data::DockerMessage;
// this is the entry point when running as a Docker Container, and is used, in conjunction with the `CONTAINER_ENV` ENV, to check if we are running as a Docker Container // this is the entry point when running as a Docker Container, and is used, in conjunction with the `CONTAINER_ENV` ENV, to check if we are running as a Docker Container
const ENTRY_POINT: &str = "./app/oxker"; const ENTRY_POINT: &str = "/app/oxker";
const ENV_KEY: &str = "OXKER_RUNTIME"; const ENV_KEY: &str = "OXKER_RUNTIME";
const ENV_VALUE: &str = "container"; const ENV_VALUE: &str = "container";
@@ -132,9 +132,7 @@ async fn main() {
handler_init(&app_data, &docker_sx, &gui_state, input_rx, &is_running); handler_init(&app_data, &docker_sx, &gui_state, input_rx, &is_running);
if args.gui { if args.gui {
create_ui(app_data, docker_sx, gui_state, is_running, input_sx) Ui::create(app_data, docker_sx, gui_state, is_running, input_sx).await;
.await
.unwrap_or(());
} else { } else {
// Debug mode for testing, mostly pointless, doesn't take terminal // Debug mode for testing, mostly pointless, doesn't take terminal
info!("in debug mode"); info!("in debug mode");
+234 -66
View File
@@ -481,93 +481,253 @@ pub fn heading_bar<B: Backend>(
/// From a given &str, return the maximum number of chars on a single line /// From a given &str, return the maximum number of chars on a single line
fn max_line_width(text: &str) -> usize { fn max_line_width(text: &str) -> usize {
let mut max_line_width = 0; text.lines()
text.lines().into_iter().for_each(|line| { .into_iter()
let width = line.chars().count(); .map(|i| i.chars().count())
if width > max_line_width { .max()
max_line_width = width; .unwrap_or_default()
}
/// Help popup box needs these three pieces of information
struct HelpInfo {
spans: Vec<Spans<'static>>,
width: usize,
height: usize,
}
impl HelpInfo {
/// Find the max width of a Span in &[Spans], although it isn't calculating it correctly
fn calc_width(spans: &[Spans]) -> usize {
spans
.iter()
.flat_map(|x| x.0.iter())
.map(tui::text::Span::width)
.max()
.unwrap_or(1)
}
/// Just an empty span, i.e. a new line
fn empty_span<'a>() -> Spans<'a> {
Spans::from(String::new())
}
/// generate a span, of given &str and given color
fn span<'a>(input: &str, color: Color) -> Span<'a> {
Span::styled(input.to_owned(), Style::default().fg(color))
}
/// Span to black text span
fn black_span<'a>(input: &str) -> Span<'a> {
Self::span(input, Color::Black)
}
/// Span to white text span
fn white_span<'a>(input: &str) -> Span<'a> {
Self::span(input, Color::White)
}
/// Generate the `oxker` name span + metadata
fn gen_name() -> Self {
let mut spans = NAME_TEXT
.lines()
.into_iter()
.map(|i| Spans::from(Self::white_span(i)))
.collect::<Vec<_>>();
spans.insert(0, Self::empty_span());
let width = Self::calc_width(&spans);
let height = spans.len();
Self {
spans,
width,
height,
}
}
/// Generate the description span + metadata
fn gen_description() -> Self {
let spans = [
Self::empty_span(),
Spans::from(Self::white_span(DESCRIPTION)),
Self::empty_span(),
];
let width = Self::calc_width(&spans);
let height = spans.len();
Self {
spans: spans.to_vec(),
width,
height,
}
}
/// Generate the button information span + metadata
fn gen_button() -> Self {
let button_item = |x: &str| Self::white_span(&format!(" {x} "));
let button_desc = |x: &str| Self::black_span(x);
let or = || button_desc("or");
let space = || button_desc(" ");
let spans = [
Spans::from(vec![
space(),
button_item("( tab )"),
or(),
button_item("( shift+tab )"),
button_desc("to change panels"),
]),
Spans::from(vec![
space(),
button_item("( ↑ ↓ )"),
or(),
button_item("( j k )"),
or(),
button_item("( PgUp PgDown )"),
or(),
button_item("( Home End )"),
button_desc("to change selected line"),
]),
Spans::from(vec![
space(),
button_item("( enter )"),
button_desc("to send docker container command"),
]),
Spans::from(vec![
space(),
button_item("( h )"),
button_desc("to toggle this help information"),
]),
Spans::from(vec![
space(),
button_item("( 0 )"),
button_desc("to stop sort"),
]),
Spans::from(vec![
space(),
button_item("( 1 - 9 )"),
button_desc("sort by header - or click header"),
]),
Spans::from(vec![
space(),
button_item("( m )"),
button_desc(
"to toggle mouse capture - if disabled, text on screen can be selected & copied",
),
]),
Spans::from(vec![
space(),
button_item("( q )"),
button_desc("to quit at any time"),
]),
];
let height = spans.len();
let width = Self::calc_width(&spans);
Self {
spans: spans.to_vec(),
width,
height,
}
}
/// Generate the final lines, GitHub link etc, + metadata
fn gen_final() -> Self {
let spans = [
Self::empty_span(),
Spans::from(vec![Self::black_span(
"currently an early work in progress, all and any input appreciated",
)]),
Spans::from(vec![Span::styled(
REPO.to_owned(),
Style::default()
.bg(Color::Magenta)
.fg(Color::Black)
.add_modifier(Modifier::UNDERLINED),
)]),
];
let height = spans.len();
let width = Self::calc_width(&spans);
Self {
spans: spans.to_vec(),
width,
height,
}
} }
});
max_line_width
} }
/// Draw the help box in the centre of the screen /// Draw the help box in the centre of the screen
/// TODO should make every line it's own renderable span
pub fn help_box<B: Backend>(f: &mut Frame<'_, B>) { pub fn help_box<B: Backend>(f: &mut Frame<'_, B>) {
let title = format!(" {VERSION} "); let title = format!(" {VERSION} ");
let description_text = format!("\n{DESCRIPTION}"); let name_info = HelpInfo::gen_name();
let description_info = HelpInfo::gen_description();
let button_info = HelpInfo::gen_button();
let final_info = HelpInfo::gen_final();
let mut help_text = String::from("\n ( tab ) or ( shift+tab ) to change panels"); // have to add 10, but shouldn't need to, is an error somewhere
help_text let max_line_width = [
.push_str("\n ( ↑ ↓ ) or ( j k ) or (PgUp PgDown) or (Home End) to change selected line"); name_info.width,
help_text.push_str("\n ( enter ) to send docker container commands"); description_info.width,
help_text.push_str("\n ( h ) to toggle this help information"); button_info.width,
help_text.push_str("\n ( 0 ) stop sort"); final_info.width,
help_text.push_str("\n ( 1 - 9 ) sort by header - or click header"); ]
help_text.push_str( .into_iter()
"\n ( m ) to toggle mouse capture - if disabled, text on screen can be selected & copied", .max()
.unwrap_or_default()
+ 10;
let max_height =
name_info.height + description_info.height + button_info.height + final_info.height + 2;
let area = popup(
max_height,
max_line_width,
f.size(),
BoxLocation::MiddleCentre,
); );
help_text.push_str("\n ( q ) to quit at any time");
help_text.push_str("\n mouse scrolling & clicking also available");
help_text.push_str("\n\n currently an early work in progress, all and any input appreciated");
help_text.push_str(format!("\n {}", REPO.trim()).as_str());
// Find the maximum line widths & height let split_popup = Layout::default()
let all_text = format!("{NAME_TEXT}{description_text}{help_text}"); .direction(Direction::Vertical)
let mut max_line_width = max_line_width(&all_text); .constraints(
let mut lines = all_text.lines().count(); [
Constraint::Max(name_info.height.try_into().unwrap_or_default()),
Constraint::Max(description_info.height.try_into().unwrap_or_default()),
Constraint::Max(button_info.height.try_into().unwrap_or_default()),
Constraint::Max(final_info.height.try_into().unwrap_or_default()),
]
.as_ref(),
)
.split(area);
// Add some vertical and horizontal padding to the info box let name_paragraph = Paragraph::new(name_info.spans)
lines += 3;
max_line_width += 4;
let name_paragraph = Paragraph::new(NAME_TEXT)
.style(Style::default().bg(Color::Magenta).fg(Color::White)) .style(Style::default().bg(Color::Magenta).fg(Color::White))
.block(Block::default()) .block(Block::default())
.alignment(Alignment::Center); .alignment(Alignment::Center);
let description_paragraph = Paragraph::new(description_text.as_str()) let description_paragraph = Paragraph::new(description_info.spans)
.style(Style::default().bg(Color::Magenta).fg(Color::Black)) .style(Style::default().bg(Color::Magenta).fg(Color::Black))
.block(Block::default()) .block(Block::default())
.alignment(Alignment::Center); .alignment(Alignment::Center);
let help_paragraph = Paragraph::new(help_text.as_str()) let help_paragraph = Paragraph::new(button_info.spans)
.style(Style::default().bg(Color::Magenta).fg(Color::Black)) .style(Style::default().bg(Color::Magenta).fg(Color::Black))
.block(Block::default()) .block(Block::default())
.alignment(Alignment::Left); .alignment(Alignment::Left);
let final_paragraph = Paragraph::new(final_info.spans)
.style(Style::default().bg(Color::Magenta).fg(Color::Black))
.block(Block::default())
.alignment(Alignment::Center);
let block = Block::default() let block = Block::default()
.title(title) .title(title)
.borders(Borders::ALL) .borders(Borders::ALL)
.border_type(BorderType::Rounded) .border_type(BorderType::Rounded)
.border_style(Style::default().fg(Color::Black)); .border_style(Style::default().fg(Color::Black));
let area = popup(lines, max_line_width, f.size(), BoxLocation::MiddleCentre);
let split_popup = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Max(NAME_TEXT.lines().count().try_into().unwrap_or_default()),
Constraint::Max(
description_text
.lines()
.count()
.try_into()
.unwrap_or_default(),
),
Constraint::Max(help_text.lines().count().try_into().unwrap_or_default()),
]
.as_ref(),
)
.split(area);
// Order is important here // Order is important here
f.render_widget(Clear, area); f.render_widget(Clear, area);
f.render_widget(name_paragraph, split_popup[0]); f.render_widget(name_paragraph, split_popup[0]);
f.render_widget(description_paragraph, split_popup[1]); f.render_widget(description_paragraph, split_popup[1]);
f.render_widget(help_paragraph, split_popup[2]); f.render_widget(help_paragraph, split_popup[2]);
f.render_widget(final_paragraph, split_popup[3]);
f.render_widget(block, area); f.render_widget(block, area);
} }
@@ -640,23 +800,19 @@ pub fn info<B: Backend>(f: &mut Frame<'_, B>, text: String) {
/// draw a box in the one of the BoxLocations, based on max line width + number of lines /// draw a box in the one of the BoxLocations, based on max line width + number of lines
fn popup(text_lines: usize, text_width: usize, r: Rect, box_location: BoxLocation) -> Rect { fn popup(text_lines: usize, text_width: usize, r: Rect, box_location: BoxLocation) -> Rect {
// Make sure blank_space can't be an negative, as will crash // Make sure blank_space can't be an negative, as will crash
let blank_vertical = if usize::from(r.height) > text_lines { let calc = |x: u16, y: usize| {
(usize::from(r.height) - text_lines) / 2 (usize::from(x).checked_sub(y).map_or(1usize, |f| f))
} else { .checked_div(2)
1 .map_or(1usize, |f| f)
};
let blank_horizontal = if usize::from(r.width) > text_width {
(usize::from(r.width) - text_width) / 2
} else {
1
}; };
let v_constraints = box_location.get_vertical_constraints( let blank_vertical = calc(r.height, text_lines);
let blank_horizontal = calc(r.width, text_width);
let (h_constraints, v_constraints) = box_location.get_constraints(
blank_horizontal.try_into().unwrap_or_default(),
blank_vertical.try_into().unwrap_or_default(), blank_vertical.try_into().unwrap_or_default(),
text_lines.try_into().unwrap_or_default(), text_lines.try_into().unwrap_or_default(),
);
let h_constraints = box_location.get_horizontal_constraints(
blank_horizontal.try_into().unwrap_or_default(),
text_width.try_into().unwrap_or_default(), text_width.try_into().unwrap_or_default(),
); );
@@ -672,3 +828,15 @@ fn popup(text_lines: usize, text_width: usize, r: Rect, box_location: BoxLocatio
.constraints(h_constraints) .constraints(h_constraints)
.split(popup_layout[indexes.0])[indexes.1] .split(popup_layout[indexes.0])[indexes.1]
} }
// Draw nothing, as in a blank screen
// pub fn nothing<B: Backend>(f: &mut Frame<'_, B>) {
// let whole_layout = Layout::default()
// .direction(Direction::Vertical)
// .constraints([Constraint::Min(100)].as_ref())
// .split(f.size());
// let block = Block::default()
// .borders(Borders::NONE);
// f.render_widget(block, whole_layout[0]);
// }
+27 -13
View File
@@ -75,31 +75,45 @@ impl BoxLocation {
} }
} }
// Should combine with get_vertical_constraints and just return a tuple of (vc, hc)? /// Get both the vertical and hoziztonal constrains
pub const fn get_horizontal_constraints( pub const fn get_constraints(
self, self,
blank_horizontal: u16,
blank_vertical: u16, blank_vertical: u16,
text_lines: u16,
text_width: u16,
) -> ([Constraint; 3], [Constraint; 3]) {
(
Self::get_horizontal_constraints(self, blank_horizontal, text_width),
Self::get_vertical_constraints(self, blank_vertical, text_lines),
)
}
const fn get_horizontal_constraints(
self,
blank_horizontal: u16,
text_width: u16, text_width: u16,
) -> [Constraint; 3] { ) -> [Constraint; 3] {
match self { match self {
Self::TopLeft | Self::MiddleLeft | Self::BottomLeft => [ Self::TopLeft | Self::MiddleLeft | Self::BottomLeft => [
Constraint::Max(text_width), Constraint::Max(text_width),
Constraint::Max(blank_vertical), Constraint::Max(blank_horizontal),
Constraint::Max(blank_vertical), Constraint::Max(blank_horizontal),
], ],
Self::TopCentre | Self::MiddleCentre | Self::BottomCentre => [ Self::TopCentre | Self::MiddleCentre | Self::BottomCentre => [
Constraint::Max(blank_vertical), Constraint::Max(blank_horizontal),
Constraint::Max(text_width), Constraint::Max(text_width),
Constraint::Max(blank_vertical), Constraint::Max(blank_horizontal),
], ],
Self::TopRight | Self::MiddleRight | Self::BottomRight => [ Self::TopRight | Self::MiddleRight | Self::BottomRight => [
Constraint::Max(blank_vertical), Constraint::Max(blank_horizontal),
Constraint::Max(blank_vertical), Constraint::Max(blank_horizontal),
Constraint::Max(text_width), Constraint::Max(text_width),
], ],
} }
} }
pub const fn get_vertical_constraints(
const fn get_vertical_constraints(
self, self,
blank_vertical: u16, blank_vertical: u16,
number_lines: u16, number_lines: u16,
@@ -188,13 +202,13 @@ pub enum Status {
/// Global gui_state, stored in an Arc<Mutex> /// Global gui_state, stored in an Arc<Mutex>
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct GuiState { pub struct GuiState {
panel_map: HashMap<SelectablePanel, Rect>,
heading_map: HashMap<Header, Rect>, heading_map: HashMap<Header, Rect>,
loading_icon: Loading,
is_loading: HashSet<Uuid>, is_loading: HashSet<Uuid>,
loading_icon: Loading,
panel_map: HashMap<SelectablePanel, Rect>,
status: HashSet<Status>, status: HashSet<Status>,
pub selected_panel: SelectablePanel,
pub info_box_text: Option<String>, pub info_box_text: Option<String>,
pub selected_panel: SelectablePanel,
} }
impl GuiState { impl GuiState {
/// Clear panels hash map, so on resize can fix the sizes for mouse clicks /// Clear panels hash map, so on resize can fix the sizes for mouse clicks
@@ -273,7 +287,7 @@ impl GuiState {
self.is_loading.insert(uuid); self.is_loading.insert(uuid);
} }
/// If is_loading has any entries, return the current loading_icon, else an empty string /// If is_loading has any entries, return the current loading_icon, else an empty string, which needs to take up the same space, hence ' '
pub fn get_loading(&mut self) -> String { pub fn get_loading(&mut self) -> String {
if self.is_loading.is_empty() { if self.is_loading.is_empty() {
String::from(" ") String::from(" ")
+133 -62
View File
@@ -1,16 +1,18 @@
use anyhow::Result; use anyhow::Result;
use crossterm::{ use crossterm::{
event::{self, DisableMouseCapture, EnableMouseCapture, Event}, event::{self, DisableMouseCapture, Event},
execute, execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{ use std::{
io::{self, Write}, io::{self, Stdout, Write},
sync::{atomic::Ordering, Arc}, sync::{atomic::Ordering, Arc},
time::Duration,
}; };
use std::{sync::atomic::AtomicBool, time::Instant}; use std::{sync::atomic::AtomicBool, time::Instant};
use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::Sender;
use tracing::error;
use tui::{ use tui::{
backend::{Backend, CrosstermBackend}, backend::{Backend, CrosstermBackend},
layout::{Constraint, Direction, Layout}, layout::{Constraint, Direction, Layout},
@@ -28,110 +30,179 @@ use crate::{
input_handler::InputMessages, input_handler::InputMessages,
}; };
/// Take control of the terminal in order to draw gui pub struct Ui {
pub async fn create_ui(
app_data: Arc<Mutex<AppData>>, app_data: Arc<Mutex<AppData>>,
docker_sx: Sender<DockerMessage>, docker_sx: Sender<DockerMessage>,
gui_state: Arc<Mutex<GuiState>>, gui_state: Arc<Mutex<GuiState>>,
input_poll_rate: Duration,
is_running: Arc<AtomicBool>, is_running: Arc<AtomicBool>,
now: Instant,
sender: Sender<InputMessages>, sender: Sender<InputMessages>,
) -> Result<()> { terminal: Terminal<CrosstermBackend<Stdout>>,
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let res = run_app(
app_data,
docker_sx,
gui_state,
is_running,
sender,
&mut terminal,
)
.await;
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
println!("error: {err}");
} }
std::io::stdout().flush().unwrap_or(());
impl Ui {
/// Enable mouse capture, but don't enable capture of all the mouse movements, doing so will improve performance, and is part of the fix for the weird mouse event output bug
pub fn enable_mouse_capture() -> Result<()> {
io::stdout().write_all(
concat!(
crossterm::csi!("?1000h"),
crossterm::csi!("?1015h"),
crossterm::csi!("?1006h"),
)
.as_bytes(),
)?;
Ok(()) Ok(())
} }
/// Run a loop to draw the gui /// Create a new Ui struct, and execute the drawing loop
async fn run_app<B: Backend + Send>( pub async fn create(
app_data: Arc<Mutex<AppData>>, app_data: Arc<Mutex<AppData>>,
docker_sx: Sender<DockerMessage>, docker_sx: Sender<DockerMessage>,
gui_state: Arc<Mutex<GuiState>>, gui_state: Arc<Mutex<GuiState>>,
is_running: Arc<AtomicBool>, is_running: Arc<AtomicBool>,
sender: Sender<InputMessages>, sender: Sender<InputMessages>,
terminal: &mut Terminal<B>, ) {
) -> Result<(), AppError> { if let Ok(terminal) = Self::setup_terminal() {
let update_duration = let mut ui = Self {
std::time::Duration::from_millis(u64::from(app_data.lock().args.docker_interval)); app_data,
let input_poll_rate = std::time::Duration::from_millis(75); docker_sx,
let status_dockerconnect = gui_state.lock().status_contains(&[Status::DockerConnect]); gui_state,
let mut now = Instant::now(); input_poll_rate: std::time::Duration::from_millis(100),
if status_dockerconnect { is_running,
now: Instant::now(),
sender,
terminal,
};
if let Err(e) = ui.draw_ui().await {
error!("{e}");
}
if let Err(e) = ui.reset_terminal() {
error!("{e}");
};
} else {
error!("Terminal Error");
}
}
/// Setup the terminal for full-screen drawing mode, with mouse capture
fn setup_terminal() -> Result<Terminal<CrosstermBackend<Stdout>>> {
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
Self::enable_mouse_capture()?;
let backend = CrosstermBackend::new(stdout);
Ok(Terminal::new(backend)?)
}
/// This is a fix for mouse-events being printed to screen, read an event and do nothing with it
fn nullify_event_read(&self) {
if crossterm::event::poll(self.input_poll_rate).unwrap_or(true) {
event::read().ok();
}
}
/// reset the terminal back to default settings
pub fn reset_terminal(&mut self) -> Result<()> {
self.terminal.clear()?;
execute!(
self.terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
disable_raw_mode()?;
self.terminal.show_cursor()?;
Ok(())
}
/// Draw the the error message ui, for 5 seconds, with a countdown
fn err_loop(&mut self) -> Result<(), AppError> {
let mut seconds = 5; let mut seconds = 5;
loop { loop {
if self.now.elapsed() >= std::time::Duration::from_secs(1) {
seconds -= 1;
self.now = Instant::now();
if seconds < 1 { if seconds < 1 {
break; break;
} }
if now.elapsed() >= std::time::Duration::from_secs(1) {
seconds -= 1;
now = Instant::now();
} }
if terminal
// This is a fix for mouse-events being printed to screen
self.nullify_event_read();
if self
.terminal
.draw(|f| draw_blocks::error(f, AppError::DockerConnect, Some(seconds))) .draw(|f| draw_blocks::error(f, AppError::DockerConnect, Some(seconds)))
.is_err() .is_err()
{ {
return Err(AppError::Terminal); return Err(AppError::Terminal);
} }
} }
} else { Ok(())
while is_running.load(Ordering::SeqCst) { }
if crossterm::event::poll(input_poll_rate).unwrap_or(false) {
/// The loop for drawing the main UI to the terminal
async fn gui_loop(&mut self) -> Result<(), AppError> {
let update_duration =
std::time::Duration::from_millis(u64::from(self.app_data.lock().args.docker_interval));
while self.is_running.load(Ordering::SeqCst) {
if self
.terminal
.draw(|frame| draw_frame(frame, &self.app_data, &self.gui_state))
.is_err()
{
return Err(AppError::Terminal);
}
if crossterm::event::poll(self.input_poll_rate).unwrap_or(false) {
if let Ok(event) = event::read() { if let Ok(event) = event::read() {
if let Event::Key(key) = event { if let Event::Key(key) = event {
sender self.sender
.send(InputMessages::ButtonPress(key.code)) .send(InputMessages::ButtonPress(key.code))
.await .await
.unwrap_or(()); .unwrap_or(());
} else if let Event::Mouse(m) = event { } else if let Event::Mouse(m) = event {
sender self.sender
.send(InputMessages::MouseEvent(m)) .send(InputMessages::MouseEvent(m))
.await .await
.unwrap_or(()); .unwrap_or(());
} else if let Event::Resize(_, _) = event { } else if let Event::Resize(_, _) = event {
gui_state.lock().clear_area_map(); self.gui_state.lock().clear_area_map();
terminal.autoresize().unwrap_or(()); self.terminal.autoresize().unwrap_or(());
} }
} }
} }
if now.elapsed() >= update_duration { if self.now.elapsed() >= update_duration {
docker_sx.send(DockerMessage::Update).await.unwrap_or(()); self.docker_sx
now = Instant::now(); .send(DockerMessage::Update)
} .await
if terminal.draw(|f| ui(f, &app_data, &gui_state)).is_err() { .unwrap_or(());
return Err(AppError::Terminal); self.now = Instant::now();
} }
} }
}
terminal.clear().unwrap_or(());
Ok(()) Ok(())
} }
fn ui<B: Backend>( /// Draw either the Error, or main oxker ui, to the terminal
async fn draw_ui(&mut self) -> Result<(), AppError> {
let status_dockerconnect = self
.gui_state
.lock()
.status_contains(&[Status::DockerConnect]);
if status_dockerconnect {
self.err_loop()?;
} else {
self.gui_loop().await?;
}
self.nullify_event_read();
Ok(())
}
}
/// Draw the main ui to a frame of the terminal
fn draw_frame<B: Backend>(
f: &mut Frame<'_, B>, f: &mut Frame<'_, B>,
app_data: &Arc<Mutex<AppData>>, app_data: &Arc<Mutex<AppData>>,
gui_state: &Arc<Mutex<GuiState>>, gui_state: &Arc<Mutex<GuiState>>,
@@ -183,7 +254,7 @@ fn ui<B: Backend>(
vec![Constraint::Percentage(100)] vec![Constraint::Percentage(100)]
}; };
// Split into 3, containers+controls, logs, then graphs // Split into 2, logs, and optional charts
let lower_main = Layout::default() let lower_main = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints(lower_split.as_ref()) .constraints(lower_split.as_ref())