mirror of
https://github.com/ClementTsang/bottom
synced 2024-11-22 12:13:06 +00:00
feature: Add battery widget (#120)
This commit is contained in:
parent
45e9ba1234
commit
163f6823a2
20 changed files with 759 additions and 232 deletions
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -11,16 +11,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- [#58](https://github.com/ClementTsang/bottom/issues/58): I/O stats per process
|
||||
|
||||
- [#55](https://github.com/ClementTsang/bottom/issues/55): Battery monitoring widget
|
||||
|
||||
- [#114](https://github.com/ClementTsang/bottom/pull/114): Process state per process
|
||||
|
||||
### Changes
|
||||
|
||||
- Changed default colours for highlighted borders and table headers to cyan - this is mostly to deal with Powershell colour conflicts.
|
||||
|
||||
- Updated the widget type keyword list to accept the following keywords as existing types:
|
||||
- `"memory"`
|
||||
- `"network"`
|
||||
- `"process"`
|
||||
- `"processes"`
|
||||
- `"temperature"`
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed `dd` not working on non-first entries.
|
||||
|
||||
- Fixed bug where a single empty row as a layout would crash without a proper warning.
|
||||
The behaviour now errors out with a more helpful message.
|
||||
|
||||
## [0.3.0] - 2020-04-07
|
||||
|
||||
### Features
|
||||
|
|
98
Cargo.lock
generated
98
Cargo.lock
generated
|
@ -83,6 +83,22 @@ name = "base64"
|
|||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "battery"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uom 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
|
@ -104,9 +120,10 @@ version = "0.3.0"
|
|||
dependencies = [
|
||||
"assert_cmd 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"backtrace 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"battery 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossterm 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossterm 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fern 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -178,6 +195,15 @@ name = "constant_time_eq"
|
|||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.7.0"
|
||||
|
@ -187,6 +213,11 @@ dependencies = [
|
|||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.7.0"
|
||||
|
@ -245,14 +276,14 @@ dependencies = [
|
|||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"signal-hook 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.17.2"
|
||||
version = "0.17.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -260,7 +291,7 @@ dependencies = [
|
|||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"signal-hook 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
@ -670,6 +701,11 @@ name = "lazy_static"
|
|||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.68"
|
||||
|
@ -677,7 +713,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -696,6 +732,14 @@ name = "macaddr"
|
|||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.3.2"
|
||||
|
@ -761,6 +805,18 @@ dependencies = [
|
|||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.17.0"
|
||||
|
@ -837,16 +893,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.10.0"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot_core 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.7.0"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1192,6 +1248,15 @@ name = "unicode-xid"
|
|||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "uom"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uom"
|
||||
version = "0.27.0"
|
||||
|
@ -1280,6 +1345,7 @@ dependencies = [
|
|||
"checksum backtrace 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)" = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e"
|
||||
"checksum backtrace-sys 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "7de8aba10a69c8e8d7622c5710229485ec32e9d55fdad160ea559c086fdcd118"
|
||||
"checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
|
||||
"checksum battery 0.7.5 (registry+https://github.com/rust-lang/crates.io-index)" = "36a698e449024a5d18994a815998bf5e2e4bc1883e35a7d7ba95b6b69ee45907"
|
||||
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
"checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
|
||||
"checksum cassowary 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||
|
@ -1289,14 +1355,16 @@ dependencies = [
|
|||
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
|
||||
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||
"checksum constant_time_eq 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
|
||||
"checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
|
||||
"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
|
||||
"checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
|
||||
"checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
|
||||
"checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
|
||||
"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
|
||||
"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||
"checksum crossterm 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5750773d74a7dc612eac2ded3f55e9cdeeaa072210cd17c0192aedb48adb3618"
|
||||
"checksum crossterm 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d2cfea8393092f9ffcfa5f1f88e2fa27b3cf5e47cb175e6b8c41ec914680b8e"
|
||||
"checksum crossterm 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ccdd8ef63a44e821956c6a276eca0faaa889d6a067dfcdbd5bfe85dce3a1d250"
|
||||
"checksum crossterm_winapi 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8777c700901e2d5b50c406f736ed6b8f9e43645c7e104ddb74f8bc42b8ae62f6"
|
||||
"checksum crossterm_winapi 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "057b7146d02fb50175fd7dbe5158f6097f33d02831f43b4ee8ae4ddf67b68f5c"
|
||||
"checksum darwin-libproc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9fb90051930c9a0f09e585762152048e23ac74d20c10590ef7cf01c0343c3046"
|
||||
|
@ -1338,10 +1406,12 @@ dependencies = [
|
|||
"checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
|
||||
"checksum libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)" = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0"
|
||||
"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b"
|
||||
"checksum lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
|
||||
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
"checksum macaddr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "baee0bbc17ce759db233beb01648088061bf678383130602a298e6998eedb2d8"
|
||||
"checksum mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1"
|
||||
"checksum mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
|
||||
"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
||||
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
|
@ -1349,6 +1419,7 @@ dependencies = [
|
|||
"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f"
|
||||
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
|
||||
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
|
||||
"checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229"
|
||||
"checksum nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
|
||||
"checksum normalize-line-endings 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
|
||||
"checksum ntapi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f26e041cd983acbc087e30fcba770380cfa352d0e392e175b2344ebaf7ea0602"
|
||||
|
@ -1358,8 +1429,8 @@ dependencies = [
|
|||
"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
|
||||
"checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b"
|
||||
"checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518"
|
||||
"checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc"
|
||||
"checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1"
|
||||
"checksum parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
|
||||
"checksum parking_lot_core 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e136c1904604defe99ce5fd71a28d473fa60a12255d511aa78a9ddf11237aeb"
|
||||
"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
|
||||
"checksum platforms 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "feb3b2b1033b8a60b4da6ee470325f887758c95d5320f52f9ce0df055a55940e"
|
||||
"checksum predicates 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "347a1b6f0b21e636bc9872fb60b83b8e185f6f5516298b8238699f7f9a531030"
|
||||
|
@ -1403,6 +1474,7 @@ dependencies = [
|
|||
"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
|
||||
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
|
||||
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
"checksum uom 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4cec796ec5f7ac557631709079168286056205c51c60aac33f51764bdc7b8dc4"
|
||||
"checksum uom 0.27.0 (registry+https://github.com/rust-lang/crates.io-index)" = "51fc04fb44bcb7806da71885872cb15d123b681e459a476ef8a0bab287bee0cd"
|
||||
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
|
|
@ -22,6 +22,7 @@ lto = "fat"
|
|||
codegen-units = 1
|
||||
|
||||
[dependencies]
|
||||
battery = "0.7.5"
|
||||
crossterm = "0.17"
|
||||
chrono = "0.4.11"
|
||||
clap = "2.33.0"
|
||||
|
|
101
README.md
101
README.md
|
@ -29,6 +29,7 @@ A cross-platform graphical process/system monitor with a customizable interface
|
|||
- [CPU bindings](#cpu-bindings)
|
||||
- [Process bindings](#process-bindings)
|
||||
- [Process search bindings](#process-search-bindings)
|
||||
- [Battery bindings](#battery-bindings)
|
||||
- [Features](#features)
|
||||
- [Process filtering](#process-filtering)
|
||||
- [Zoom](#zoom)
|
||||
|
@ -38,6 +39,7 @@ A cross-platform graphical process/system monitor with a customizable interface
|
|||
- [Config flags](#config-flags)
|
||||
- [Theming](#theming)
|
||||
- [Layout](#layout)
|
||||
- [Battery](#battery)
|
||||
- [Compatibility](#compatibility)
|
||||
- [Contribution](#contribution)
|
||||
- [Bug reports and feature requests](#bug-reports-and-feature-requests)
|
||||
|
@ -202,6 +204,15 @@ Run using `btm`.
|
|||
| `Alt-c`/`F1` | Toggle matching case |
|
||||
| `Alt-w`/`F2` | Toggle matching the entire word |
|
||||
| `Alt-r`/`F3` | Toggle using regex |
|
||||
| `Left` | Move cursor left |
|
||||
| `Right` | Move cursor right |
|
||||
|
||||
#### Battery bindings
|
||||
|
||||
| | |
|
||||
| ------- | -------------------------- |
|
||||
| `Left` | Go to the next battery |
|
||||
| `Right` | Go to the previous battery |
|
||||
|
||||
## Features
|
||||
|
||||
|
@ -289,23 +300,24 @@ The config file can be used to set custom colours for parts of the application u
|
|||
|
||||
Supported named colours are one of the following strings: `Reset, Black, Red, Green, Yellow, Blue, Magenta, Cyan, Gray, DarkGray, LightRed, LightGreen, LightYellow, LightBlue, LightMagenta, LightCyan, White`.
|
||||
|
||||
| Labels | Details | Example |
|
||||
| ------------------------------- | ---------------------------------------------- | ------------------------------------------------------- |
|
||||
| Table header colours | Colour of table headers | `table_header_color="255, 255, 255"` |
|
||||
| CPU colour per core | Colour of each core. Read in order. | `cpu_core_colors=["#ffffff", "white", "255, 255, 255"]` |
|
||||
| Average CPU colour | The average CPU color | `avg_cpu_color="White"` |
|
||||
| RAM | The colour RAM will use | `ram_color="#ffffff"` |
|
||||
| SWAP | The colour SWAP will use | `swap_color="#ffffff"` |
|
||||
| RX | The colour rx will use | `rx_color="#ffffff"` |
|
||||
| TX | The colour tx will use | `tx_color="#ffffff"` |
|
||||
| Widget title colour | The colour of the label each widget has | `widget_title_color="#ffffff"` |
|
||||
| Border colour | The colour of the border of unselected widgets | `border_color="#ffffff"` |
|
||||
| Selected border colour | The colour of the border of selected widgets | `highlighted_border_color="#ffffff"` |
|
||||
| Text colour | The colour of most text | `text_color="#ffffff"` |
|
||||
| Graph colour | The colour of the lines and text of the graph | `graph_color="#ffffff"` |
|
||||
| Cursor colour | The cursor's colour | `cursor_color="#ffffff"` |
|
||||
| Selected text colour | The colour of text that is selected | `scroll_entry_text_color="#ffffff"` |
|
||||
| Selected text background colour | The background colour of text that is selected | `scroll_entry_bg_color="#ffffff"` |
|
||||
| Labels | Details | Example |
|
||||
| ------------------------------- | ----------------------------------------------------- | ------------------------------------------------------- |
|
||||
| Table header colours | Colour of table headers | `table_header_color="255, 255, 255"` |
|
||||
| CPU colour per core | Colour of each core. Read in order. | `cpu_core_colors=["#ffffff", "white", "255, 255, 255"]` |
|
||||
| Average CPU colour | The average CPU color | `avg_cpu_color="White"` |
|
||||
| RAM | The colour RAM will use | `ram_color="#ffffff"` |
|
||||
| SWAP | The colour SWAP will use | `swap_color="#ffffff"` |
|
||||
| RX | The colour rx will use | `rx_color="#ffffff"` |
|
||||
| TX | The colour tx will use | `tx_color="#ffffff"` |
|
||||
| Widget title colour | The colour of the label each widget has | `widget_title_color="#ffffff"` |
|
||||
| Border colour | The colour of the border of unselected widgets | `border_color="#ffffff"` |
|
||||
| Selected border colour | The colour of the border of selected widgets | `highlighted_border_color="#ffffff"` |
|
||||
| Text colour | The colour of most text | `text_color="#ffffff"` |
|
||||
| Graph colour | The colour of the lines and text of the graph | `graph_color="#ffffff"` |
|
||||
| Cursor colour | The cursor's colour | `cursor_color="#ffffff"` |
|
||||
| Selected text colour | The colour of text that is selected | `scroll_entry_text_color="#ffffff"` |
|
||||
| Selected text background colour | The background colour of text that is selected | `scroll_entry_bg_color="#ffffff"` |
|
||||
| Battery bar colours | Colour used is based on percentage and no. of colours | `battery_colours=["green", "yellow", "red"]` |
|
||||
|
||||
#### Layout
|
||||
|
||||
|
@ -345,13 +357,14 @@ represents a _widget_. A widget is represented by having a `type` field set to a
|
|||
The following `type` values are supported:
|
||||
| | |
|
||||
|---------|--------------------------|
|
||||
| `cpu` | CPU chart and legend |
|
||||
| `mem` | Memory chart |
|
||||
| `proc` | Process table and search |
|
||||
| `net` | Network chart and legend |
|
||||
| `temp` | Temperature table |
|
||||
| `disk` | Disk table |
|
||||
| `empty` | An empty space |
|
||||
| `"cpu"` | CPU chart and legend |
|
||||
| `"mem", "memory"` | Memory chart |
|
||||
| `"net", "network"` | Network chart and legend |
|
||||
| `"proc", "process", "processes"` | Process table and search |
|
||||
| `"temp", "temperature"` | Temperature table |
|
||||
| `"disk"` | Disk table |
|
||||
| `"empty"` | An empty space |
|
||||
| `"batt", "battery"` | Battery statistics |
|
||||
|
||||
Each component of the layout accepts a `ratio` value. If this is not set, it defaults to 1.
|
||||
|
||||
|
@ -389,15 +402,45 @@ For an example, look at the [default config](./sample_configs/default_config.tom
|
|||
and get the following CPU donut:
|
||||
![CPU donut](./assets/cpu_layout.png)
|
||||
|
||||
### Battery
|
||||
|
||||
You can get battery statistics (charge, time to fill/discharge, and consumption in watts) via the battery widget.
|
||||
Since this is only useful for devices like laptops, it is off by default. Currently, the only way to use it is to set it
|
||||
as a widget via [layouts](#layout).
|
||||
|
||||
So with this slightly silly layout:
|
||||
|
||||
```toml
|
||||
[[row]]
|
||||
ratio=1
|
||||
[[row.child]]
|
||||
type="batt"
|
||||
[[row]]
|
||||
ratio=2
|
||||
[[row.child]]
|
||||
ratio=4
|
||||
type="batt"
|
||||
[[row.child]]
|
||||
ratio=3
|
||||
[[row.child.child]]
|
||||
type="cpu"
|
||||
[[row.child.child]]
|
||||
type="batt"
|
||||
```
|
||||
|
||||
You get this:
|
||||
|
||||
![Battery example](assets/battery.png)
|
||||
|
||||
### Compatibility
|
||||
|
||||
The current compatibility of widgets with operating systems from personal testing:
|
||||
|
||||
| OS | CPU | Memory | Disks | Temperature | Processes/Search | Networks |
|
||||
| ------- | --- | ------ | ----- | ----------- | ---------------- | -------- |
|
||||
| Linux | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Windows | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ |
|
||||
| macOS | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| OS | CPU | Memory | Disks | Temperature | Processes/Search | Networks | Battery |
|
||||
| ------- | --- | ------ | ----- | ----------- | ---------------- | -------- | -------------------------------------------- |
|
||||
| Linux | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| Windows | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ | ✓ (seems to have issues with dual batteries) |
|
||||
| macOS | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
|
||||
## Contribution
|
||||
|
||||
|
|
BIN
assets/battery.png
Normal file
BIN
assets/battery.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 MiB |
147
src/app.rs
147
src/app.rs
|
@ -452,7 +452,6 @@ impl DiskState {
|
|||
DiskState { widget_states }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BasicTableWidgetState {
|
||||
// Since this is intended (currently) to only be used for ONE widget, that's
|
||||
// how it's going to be written. If we want to allow for multiple of these,
|
||||
|
@ -462,6 +461,21 @@ pub struct BasicTableWidgetState {
|
|||
pub widget_id: i64,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BatteryWidgetState {
|
||||
pub currently_selected_battery_index: usize,
|
||||
}
|
||||
|
||||
pub struct BatteryState {
|
||||
pub widget_states: HashMap<u64, BatteryWidgetState>,
|
||||
}
|
||||
|
||||
impl BatteryState {
|
||||
pub fn init(widget_states: HashMap<u64, BatteryWidgetState>) -> Self {
|
||||
BatteryState { widget_states }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(TypedBuilder)]
|
||||
pub struct App {
|
||||
#[builder(default = false, setter(skip))]
|
||||
|
@ -506,6 +520,7 @@ pub struct App {
|
|||
pub proc_state: ProcState,
|
||||
pub temp_state: TempState,
|
||||
pub disk_state: DiskState,
|
||||
pub battery_state: BatteryState,
|
||||
|
||||
pub basic_table_widget_state: Option<BasicTableWidgetState>,
|
||||
|
||||
|
@ -1089,33 +1104,50 @@ impl App {
|
|||
|
||||
pub fn on_left_key(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if let BottomWidgetType::ProcSearch = self.current_widget.widget_type {
|
||||
let is_in_search_widget = self.is_in_search_widget();
|
||||
if let Some(proc_widget_state) = self
|
||||
.proc_state
|
||||
.widget_states
|
||||
.get_mut(&(self.current_widget.widget_id - 1))
|
||||
{
|
||||
if is_in_search_widget {
|
||||
let prev_cursor = proc_widget_state.get_cursor_position();
|
||||
proc_widget_state.search_walk_back(proc_widget_state.get_cursor_position());
|
||||
if proc_widget_state.get_cursor_position() < prev_cursor {
|
||||
let str_slice = &proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.current_search_query
|
||||
[proc_widget_state.get_cursor_position()..prev_cursor];
|
||||
match self.current_widget.widget_type {
|
||||
BottomWidgetType::ProcSearch => {
|
||||
let is_in_search_widget = self.is_in_search_widget();
|
||||
if let Some(proc_widget_state) = self
|
||||
.proc_state
|
||||
.widget_states
|
||||
.get_mut(&(self.current_widget.widget_id - 1))
|
||||
{
|
||||
if is_in_search_widget {
|
||||
let prev_cursor = proc_widget_state.get_cursor_position();
|
||||
proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.char_cursor_position -= UnicodeWidthStr::width(str_slice);
|
||||
proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.cursor_direction = CursorDirection::LEFT;
|
||||
.search_walk_back(proc_widget_state.get_cursor_position());
|
||||
if proc_widget_state.get_cursor_position() < prev_cursor {
|
||||
let str_slice = &proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.current_search_query
|
||||
[proc_widget_state.get_cursor_position()..prev_cursor];
|
||||
proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.char_cursor_position -= UnicodeWidthStr::width(str_slice);
|
||||
proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.cursor_direction = CursorDirection::LEFT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BottomWidgetType::Battery => {
|
||||
if !self.canvas_data.battery_data.is_empty() {
|
||||
if let Some(battery_widget_state) = self
|
||||
.battery_state
|
||||
.widget_states
|
||||
.get_mut(&self.current_widget.widget_id)
|
||||
{
|
||||
if battery_widget_state.currently_selected_battery_index > 0 {
|
||||
battery_widget_state.currently_selected_battery_index -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else if self.delete_dialog_state.is_showing_dd && !self.delete_dialog_state.is_on_yes {
|
||||
self.delete_dialog_state.is_on_yes = true;
|
||||
|
@ -1124,34 +1156,53 @@ impl App {
|
|||
|
||||
pub fn on_right_key(&mut self) {
|
||||
if !self.is_in_dialog() {
|
||||
if let BottomWidgetType::ProcSearch = self.current_widget.widget_type {
|
||||
let is_in_search_widget = self.is_in_search_widget();
|
||||
if let Some(proc_widget_state) = self
|
||||
.proc_state
|
||||
.widget_states
|
||||
.get_mut(&(self.current_widget.widget_id - 1))
|
||||
{
|
||||
if is_in_search_widget {
|
||||
let prev_cursor = proc_widget_state.get_cursor_position();
|
||||
proc_widget_state
|
||||
.search_walk_forward(proc_widget_state.get_cursor_position());
|
||||
if proc_widget_state.get_cursor_position() > prev_cursor {
|
||||
let str_slice = &proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.current_search_query
|
||||
[prev_cursor..proc_widget_state.get_cursor_position()];
|
||||
match self.current_widget.widget_type {
|
||||
BottomWidgetType::ProcSearch => {
|
||||
let is_in_search_widget = self.is_in_search_widget();
|
||||
if let Some(proc_widget_state) = self
|
||||
.proc_state
|
||||
.widget_states
|
||||
.get_mut(&(self.current_widget.widget_id - 1))
|
||||
{
|
||||
if is_in_search_widget {
|
||||
let prev_cursor = proc_widget_state.get_cursor_position();
|
||||
proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.char_cursor_position += UnicodeWidthStr::width(str_slice);
|
||||
proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.cursor_direction = CursorDirection::RIGHT;
|
||||
.search_walk_forward(proc_widget_state.get_cursor_position());
|
||||
if proc_widget_state.get_cursor_position() > prev_cursor {
|
||||
let str_slice = &proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.current_search_query
|
||||
[prev_cursor..proc_widget_state.get_cursor_position()];
|
||||
proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.char_cursor_position += UnicodeWidthStr::width(str_slice);
|
||||
proc_widget_state
|
||||
.process_search_state
|
||||
.search_state
|
||||
.cursor_direction = CursorDirection::RIGHT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BottomWidgetType::Battery => {
|
||||
if !self.canvas_data.battery_data.is_empty() {
|
||||
let battery_count = self.canvas_data.battery_data.len();
|
||||
if let Some(battery_widget_state) = self
|
||||
.battery_state
|
||||
.widget_states
|
||||
.get_mut(&self.current_widget.widget_id)
|
||||
{
|
||||
if battery_widget_state.currently_selected_battery_index
|
||||
< battery_count - 1
|
||||
{
|
||||
battery_widget_state.currently_selected_battery_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else if self.delete_dialog_state.is_showing_dd && self.delete_dialog_state.is_on_yes {
|
||||
self.delete_dialog_state.is_on_yes = false;
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
use std::time::Instant;
|
||||
use std::vec::Vec;
|
||||
|
||||
use crate::data_harvester::{cpu, disks, mem, network, processes, temperature, Data};
|
||||
use crate::data_harvester::{
|
||||
battery_harvester, cpu, disks, mem, network, processes, temperature, Data,
|
||||
};
|
||||
|
||||
pub type TimeOffset = f64;
|
||||
pub type Value = f64;
|
||||
|
@ -56,6 +58,7 @@ pub struct DataCollection {
|
|||
pub io_harvest: disks::IOHarvest,
|
||||
pub io_labels_and_prev: Vec<((u64, u64), (u64, u64))>,
|
||||
pub temp_harvest: Vec<temperature::TempHarvest>,
|
||||
pub battery_harvest: Vec<battery_harvester::BatteryHarvest>,
|
||||
}
|
||||
|
||||
impl Default for DataCollection {
|
||||
|
@ -73,6 +76,7 @@ impl Default for DataCollection {
|
|||
io_harvest: disks::IOHarvest::default(),
|
||||
io_labels_and_prev: Vec::default(),
|
||||
temp_harvest: Vec::default(),
|
||||
battery_harvest: Vec::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +93,7 @@ impl DataCollection {
|
|||
self.io_harvest = disks::IOHarvest::default();
|
||||
self.io_labels_and_prev = Vec::default();
|
||||
self.temp_harvest = Vec::default();
|
||||
self.battery_harvest = Vec::default();
|
||||
}
|
||||
|
||||
pub fn set_frozen_time(&mut self) {
|
||||
|
@ -115,22 +120,43 @@ impl DataCollection {
|
|||
let mut new_entry = TimedData::default();
|
||||
|
||||
// Network
|
||||
self.eat_network(&harvested_data, harvested_time, &mut new_entry);
|
||||
if let Some(network) = &harvested_data.network {
|
||||
self.eat_network(network, harvested_time, &mut new_entry);
|
||||
}
|
||||
|
||||
// Memory and Swap
|
||||
self.eat_memory_and_swap(&harvested_data, harvested_time, &mut new_entry);
|
||||
if let Some(memory) = &harvested_data.memory {
|
||||
if let Some(swap) = &harvested_data.swap {
|
||||
self.eat_memory_and_swap(memory, swap, harvested_time, &mut new_entry);
|
||||
}
|
||||
}
|
||||
|
||||
// CPU
|
||||
self.eat_cpu(&harvested_data, harvested_time, &mut new_entry);
|
||||
if let Some(cpu) = &harvested_data.cpu {
|
||||
self.eat_cpu(cpu, harvested_time, &mut new_entry);
|
||||
}
|
||||
|
||||
// Temp
|
||||
self.eat_temp(&harvested_data);
|
||||
if let Some(temperature_sensors) = &harvested_data.temperature_sensors {
|
||||
self.eat_temp(temperature_sensors);
|
||||
}
|
||||
|
||||
// Disks
|
||||
self.eat_disks(&harvested_data, harvested_time);
|
||||
if let Some(disks) = &harvested_data.disks {
|
||||
if let Some(io) = &harvested_data.io {
|
||||
self.eat_disks(disks, io, harvested_time);
|
||||
}
|
||||
}
|
||||
|
||||
// Processes
|
||||
self.eat_proc(&harvested_data);
|
||||
if let Some(list_of_processes) = &harvested_data.list_of_processes {
|
||||
self.eat_proc(list_of_processes);
|
||||
}
|
||||
|
||||
// Battery
|
||||
if let Some(list_of_batteries) = &harvested_data.list_of_batteries {
|
||||
self.eat_battery(list_of_batteries);
|
||||
}
|
||||
|
||||
// And we're done eating. Update time and push the new entry!
|
||||
self.current_instant = harvested_time;
|
||||
|
@ -138,12 +164,13 @@ impl DataCollection {
|
|||
}
|
||||
|
||||
fn eat_memory_and_swap(
|
||||
&mut self, harvested_data: &Data, harvested_time: Instant, new_entry: &mut TimedData,
|
||||
&mut self, memory: &mem::MemHarvest, swap: &mem::MemHarvest, harvested_time: Instant,
|
||||
new_entry: &mut TimedData,
|
||||
) {
|
||||
// Memory
|
||||
let mem_percent = match harvested_data.memory.mem_total_in_mb {
|
||||
let mem_percent = match memory.mem_total_in_mb {
|
||||
0 => 0f64,
|
||||
total => (harvested_data.memory.mem_used_in_mb as f64) / (total as f64) * 100.0,
|
||||
total => (memory.mem_used_in_mb as f64) / (total as f64) * 100.0,
|
||||
};
|
||||
let mem_joining_pts = if let Some((time, last_pt)) = self.timed_data_vec.last() {
|
||||
generate_joining_points(*time, last_pt.mem_data.0, harvested_time, mem_percent)
|
||||
|
@ -154,10 +181,10 @@ impl DataCollection {
|
|||
new_entry.mem_data = mem_pt;
|
||||
|
||||
// Swap
|
||||
if harvested_data.swap.mem_total_in_mb > 0 {
|
||||
let swap_percent = match harvested_data.swap.mem_total_in_mb {
|
||||
if swap.mem_total_in_mb > 0 {
|
||||
let swap_percent = match swap.mem_total_in_mb {
|
||||
0 => 0f64,
|
||||
total => (harvested_data.swap.mem_used_in_mb as f64) / (total as f64) * 100.0,
|
||||
total => (swap.mem_used_in_mb as f64) / (total as f64) * 100.0,
|
||||
};
|
||||
let swap_joining_pt = if let Some((time, last_pt)) = self.timed_data_vec.last() {
|
||||
generate_joining_points(*time, last_pt.swap_data.0, harvested_time, swap_percent)
|
||||
|
@ -169,16 +196,17 @@ impl DataCollection {
|
|||
}
|
||||
|
||||
// In addition copy over latest data for easy reference
|
||||
self.memory_harvest = harvested_data.memory.clone();
|
||||
self.swap_harvest = harvested_data.swap.clone();
|
||||
self.memory_harvest = memory.clone();
|
||||
self.swap_harvest = swap.clone();
|
||||
}
|
||||
|
||||
fn eat_network(
|
||||
&mut self, harvested_data: &Data, harvested_time: Instant, new_entry: &mut TimedData,
|
||||
&mut self, network: &network::NetworkHarvest, harvested_time: Instant,
|
||||
new_entry: &mut TimedData,
|
||||
) {
|
||||
// RX
|
||||
let logged_rx_val = if harvested_data.network.rx as f64 > 0.0 {
|
||||
(harvested_data.network.rx as f64).log(2.0)
|
||||
let logged_rx_val = if network.rx as f64 > 0.0 {
|
||||
(network.rx as f64).log(2.0)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
@ -192,8 +220,8 @@ impl DataCollection {
|
|||
new_entry.rx_data = rx_pt;
|
||||
|
||||
// TX
|
||||
let logged_tx_val = if harvested_data.network.tx as f64 > 0.0 {
|
||||
(harvested_data.network.tx as f64).log(2.0)
|
||||
let logged_tx_val = if network.tx as f64 > 0.0 {
|
||||
(network.tx as f64).log(2.0)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
@ -207,47 +235,49 @@ impl DataCollection {
|
|||
new_entry.tx_data = tx_pt;
|
||||
|
||||
// In addition copy over latest data for easy reference
|
||||
self.network_harvest = harvested_data.network.clone();
|
||||
self.network_harvest = network.clone();
|
||||
}
|
||||
|
||||
fn eat_cpu(
|
||||
&mut self, harvested_data: &Data, harvested_time: Instant, new_entry: &mut TimedData,
|
||||
&mut self, cpu: &[cpu::CPUData], harvested_time: Instant, new_entry: &mut TimedData,
|
||||
) {
|
||||
// Note this only pre-calculates the data points - the names will be
|
||||
// within the local copy of cpu_harvest. Since it's all sequential
|
||||
// it probably doesn't matter anyways.
|
||||
if let Some((time, last_pt)) = self.timed_data_vec.last() {
|
||||
for (cpu, last_pt_data) in harvested_data.cpu.iter().zip(&last_pt.cpu_data) {
|
||||
for (cpu, last_pt_data) in cpu.iter().zip(&last_pt.cpu_data) {
|
||||
let cpu_joining_pts =
|
||||
generate_joining_points(*time, last_pt_data.0, harvested_time, cpu.cpu_usage);
|
||||
let cpu_pt = (cpu.cpu_usage, cpu_joining_pts);
|
||||
new_entry.cpu_data.push(cpu_pt);
|
||||
}
|
||||
} else {
|
||||
for cpu in harvested_data.cpu.iter() {
|
||||
for cpu in cpu.iter() {
|
||||
let cpu_pt = (cpu.cpu_usage, Vec::new());
|
||||
new_entry.cpu_data.push(cpu_pt);
|
||||
}
|
||||
}
|
||||
|
||||
self.cpu_harvest = harvested_data.cpu.clone();
|
||||
self.cpu_harvest = cpu.to_vec();
|
||||
}
|
||||
|
||||
fn eat_temp(&mut self, harvested_data: &Data) {
|
||||
fn eat_temp(&mut self, temperature_sensors: &[temperature::TempHarvest]) {
|
||||
// TODO: [PO] To implement
|
||||
self.temp_harvest = harvested_data.temperature_sensors.clone();
|
||||
self.temp_harvest = temperature_sensors.to_vec();
|
||||
}
|
||||
|
||||
fn eat_disks(&mut self, harvested_data: &Data, harvested_time: Instant) {
|
||||
fn eat_disks(
|
||||
&mut self, disks: &[disks::DiskHarvest], io: &disks::IOHarvest, harvested_time: Instant,
|
||||
) {
|
||||
// TODO: [PO] To implement
|
||||
|
||||
let time_since_last_harvest = harvested_time
|
||||
.duration_since(self.current_instant)
|
||||
.as_secs_f64();
|
||||
|
||||
for (itx, device) in harvested_data.disks.iter().enumerate() {
|
||||
for (itx, device) in disks.iter().enumerate() {
|
||||
if let Some(trim) = device.name.split('/').last() {
|
||||
let io_device = harvested_data.io.get(trim);
|
||||
let io_device = io.get(trim);
|
||||
if let Some(io) = io_device {
|
||||
let io_r_pt = io.read_bytes;
|
||||
let io_w_pt = io.write_bytes;
|
||||
|
@ -267,12 +297,16 @@ impl DataCollection {
|
|||
}
|
||||
}
|
||||
|
||||
self.disk_harvest = harvested_data.disks.clone();
|
||||
self.io_harvest = harvested_data.io.clone();
|
||||
self.disk_harvest = disks.to_vec();
|
||||
self.io_harvest = io.clone();
|
||||
}
|
||||
|
||||
fn eat_proc(&mut self, harvested_data: &Data) {
|
||||
self.process_harvest = harvested_data.list_of_processes.clone();
|
||||
fn eat_proc(&mut self, list_of_processes: &[processes::ProcessHarvest]) {
|
||||
self.process_harvest = list_of_processes.to_vec();
|
||||
}
|
||||
|
||||
fn eat_battery(&mut self, list_of_batteries: &[battery_harvester::BatteryHarvest]) {
|
||||
self.battery_harvest = list_of_batteries.to_vec();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,10 +7,13 @@ use std::collections::HashMap;
|
|||
|
||||
use sysinfo::{System, SystemExt};
|
||||
|
||||
use battery::{Battery, Manager};
|
||||
|
||||
use crate::app::layout_manager::UsedWidgets;
|
||||
|
||||
use futures::join;
|
||||
|
||||
pub mod battery_harvester;
|
||||
pub mod cpu;
|
||||
pub mod disks;
|
||||
pub mod mem;
|
||||
|
@ -20,44 +23,48 @@ pub mod temperature;
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Data {
|
||||
pub cpu: cpu::CPUHarvest,
|
||||
pub memory: mem::MemHarvest,
|
||||
pub swap: mem::MemHarvest,
|
||||
pub temperature_sensors: Vec<temperature::TempHarvest>,
|
||||
pub network: network::NetworkHarvest,
|
||||
pub list_of_processes: Vec<processes::ProcessHarvest>,
|
||||
pub disks: Vec<disks::DiskHarvest>,
|
||||
pub io: disks::IOHarvest,
|
||||
pub last_collection_time: Instant,
|
||||
pub cpu: Option<cpu::CPUHarvest>,
|
||||
pub memory: Option<mem::MemHarvest>,
|
||||
pub swap: Option<mem::MemHarvest>,
|
||||
pub temperature_sensors: Option<Vec<temperature::TempHarvest>>,
|
||||
pub network: Option<network::NetworkHarvest>,
|
||||
pub list_of_processes: Option<Vec<processes::ProcessHarvest>>,
|
||||
pub disks: Option<Vec<disks::DiskHarvest>>,
|
||||
pub io: Option<disks::IOHarvest>,
|
||||
pub list_of_batteries: Option<Vec<battery_harvester::BatteryHarvest>>,
|
||||
}
|
||||
|
||||
impl Default for Data {
|
||||
fn default() -> Self {
|
||||
Data {
|
||||
cpu: cpu::CPUHarvest::default(),
|
||||
memory: mem::MemHarvest::default(),
|
||||
swap: mem::MemHarvest::default(),
|
||||
temperature_sensors: Vec::default(),
|
||||
list_of_processes: Vec::default(),
|
||||
disks: Vec::default(),
|
||||
io: disks::IOHarvest::default(),
|
||||
network: network::NetworkHarvest::default(),
|
||||
last_collection_time: Instant::now(),
|
||||
cpu: None,
|
||||
memory: None,
|
||||
swap: None,
|
||||
temperature_sensors: None,
|
||||
list_of_processes: None,
|
||||
disks: None,
|
||||
io: None,
|
||||
network: None,
|
||||
list_of_batteries: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub fn first_run_cleanup(&mut self) {
|
||||
self.io = disks::IOHarvest::default();
|
||||
self.temperature_sensors = Vec::new();
|
||||
self.list_of_processes = Vec::new();
|
||||
self.disks = Vec::new();
|
||||
self.io = None;
|
||||
self.temperature_sensors = None;
|
||||
self.list_of_processes = None;
|
||||
self.disks = None;
|
||||
self.memory = None;
|
||||
self.swap = None;
|
||||
self.cpu = None;
|
||||
|
||||
self.network.first_run_cleanup();
|
||||
self.memory = mem::MemHarvest::default();
|
||||
self.swap = mem::MemHarvest::default();
|
||||
self.cpu = cpu::CPUHarvest::default();
|
||||
if let Some(network) = &mut self.network {
|
||||
network.first_run_cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,6 +85,8 @@ pub struct DataCollector {
|
|||
total_tx: u64,
|
||||
show_average_cpu: bool,
|
||||
widgets_to_harvest: UsedWidgets,
|
||||
battery_manager: Option<Manager>,
|
||||
battery_list: Option<Vec<Battery>>,
|
||||
}
|
||||
|
||||
impl Default for DataCollector {
|
||||
|
@ -99,6 +108,8 @@ impl Default for DataCollector {
|
|||
total_tx: 0,
|
||||
show_average_cpu: false,
|
||||
widgets_to_harvest: UsedWidgets::default(),
|
||||
battery_manager: None,
|
||||
battery_list: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,6 +117,19 @@ impl Default for DataCollector {
|
|||
impl DataCollector {
|
||||
pub fn init(&mut self) {
|
||||
self.mem_total_kb = self.sys.get_total_memory();
|
||||
|
||||
if self.widgets_to_harvest.use_battery {
|
||||
if let Ok(battery_manager) = Manager::new() {
|
||||
if let Ok(batteries) = battery_manager.batteries() {
|
||||
let battery_list: Vec<Battery> = batteries.filter_map(Result::ok).collect();
|
||||
if !battery_list.is_empty() {
|
||||
self.battery_list = Some(battery_list);
|
||||
self.battery_manager = Some(battery_manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
futures::executor::block_on(self.update_data());
|
||||
std::thread::sleep(std::time::Duration::from_millis(250));
|
||||
self.data.first_run_cleanup();
|
||||
|
@ -148,7 +172,17 @@ impl DataCollector {
|
|||
|
||||
// CPU
|
||||
if self.widgets_to_harvest.use_cpu {
|
||||
self.data.cpu = cpu::get_cpu_data_list(&self.sys, self.show_average_cpu);
|
||||
self.data.cpu = Some(cpu::get_cpu_data_list(&self.sys, self.show_average_cpu));
|
||||
}
|
||||
|
||||
// Batteries
|
||||
if let Some(battery_manager) = &self.battery_manager {
|
||||
if let Some(battery_list) = &mut self.battery_list {
|
||||
self.data.list_of_batteries = Some(battery_harvester::refresh_batteries(
|
||||
&battery_manager,
|
||||
battery_list,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if self.widgets_to_harvest.use_proc {
|
||||
|
@ -186,7 +220,7 @@ impl DataCollector {
|
|||
Ok(Vec::new())
|
||||
}
|
||||
} {
|
||||
self.data.list_of_processes = process_list;
|
||||
self.data.list_of_processes = Some(process_list);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,39 +255,29 @@ impl DataCollector {
|
|||
|
||||
// After async
|
||||
if let Some(net_data) = net_data {
|
||||
self.data.network = net_data;
|
||||
self.total_rx = self.data.network.total_rx;
|
||||
self.total_tx = self.data.network.total_tx;
|
||||
self.total_rx = net_data.total_rx;
|
||||
self.total_tx = net_data.total_tx;
|
||||
self.data.network = Some(net_data);
|
||||
}
|
||||
|
||||
if let Ok(memory) = mem_res {
|
||||
if let Some(memory) = memory {
|
||||
self.data.memory = memory;
|
||||
}
|
||||
self.data.memory = memory;
|
||||
}
|
||||
|
||||
if let Ok(swap) = swap_res {
|
||||
if let Some(swap) = swap {
|
||||
self.data.swap = swap;
|
||||
}
|
||||
self.data.swap = swap;
|
||||
}
|
||||
|
||||
if let Ok(disks) = disk_res {
|
||||
if let Some(disks) = disks {
|
||||
self.data.disks = disks;
|
||||
}
|
||||
self.data.disks = disks;
|
||||
}
|
||||
|
||||
if let Ok(io) = io_res {
|
||||
if let Some(io) = io {
|
||||
self.data.io = io;
|
||||
}
|
||||
self.data.io = io;
|
||||
}
|
||||
|
||||
if let Ok(temp) = temp_res {
|
||||
if let Some(temp) = temp {
|
||||
self.data.temperature_sensors = temp;
|
||||
}
|
||||
self.data.temperature_sensors = temp;
|
||||
}
|
||||
|
||||
// Update time
|
||||
|
|
42
src/app/data_harvester/battery_harvester.rs
Normal file
42
src/app/data_harvester/battery_harvester.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use battery::{
|
||||
units::{power::watt, ratio::percent, time::second, Time},
|
||||
Battery, Manager,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BatteryHarvest {
|
||||
pub charge_percent: f64,
|
||||
pub secs_until_full: Option<i64>,
|
||||
pub secs_until_empty: Option<i64>,
|
||||
pub power_consumption_rate_watts: f64,
|
||||
}
|
||||
|
||||
fn convert_optional_time_to_optional_seconds(optional_time: Option<Time>) -> Option<i64> {
|
||||
if let Some(time) = optional_time {
|
||||
Some(f64::from(time.get::<second>()) as i64)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refresh_batteries(manager: &Manager, batteries: &mut [Battery]) -> Vec<BatteryHarvest> {
|
||||
batteries
|
||||
.iter_mut()
|
||||
.filter_map(|battery| {
|
||||
if manager.refresh(battery).is_ok() {
|
||||
Some(BatteryHarvest {
|
||||
secs_until_full: convert_optional_time_to_optional_seconds(
|
||||
battery.time_to_full(),
|
||||
),
|
||||
secs_until_empty: convert_optional_time_to_optional_seconds(
|
||||
battery.time_to_empty(),
|
||||
),
|
||||
charge_percent: f64::from(battery.state_of_charge().get::<percent>()),
|
||||
power_consumption_rate_watts: f64::from(battery.energy_rate().get::<watt>()),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
|
@ -878,6 +878,7 @@ pub enum BottomWidgetType {
|
|||
BasicMem,
|
||||
BasicNet,
|
||||
BasicTables,
|
||||
Battery,
|
||||
}
|
||||
|
||||
impl BottomWidgetType {
|
||||
|
@ -924,12 +925,13 @@ impl std::str::FromStr for BottomWidgetType {
|
|||
let lower_case = s.to_lowercase();
|
||||
match lower_case.as_str() {
|
||||
"cpu" => Ok(BottomWidgetType::Cpu),
|
||||
"mem" => Ok(BottomWidgetType::Mem),
|
||||
"net" => Ok(BottomWidgetType::Net),
|
||||
"proc" => Ok(BottomWidgetType::Proc),
|
||||
"temp" => Ok(BottomWidgetType::Temp),
|
||||
"disk" => Ok(BottomWidgetType::Disk),
|
||||
"mem" | "memory" => Ok(BottomWidgetType::Mem),
|
||||
"net" | "network" => Ok(BottomWidgetType::Net),
|
||||
"proc" | "process" | "processes" => Ok(BottomWidgetType::Proc),
|
||||
"temp" | "temperature" => Ok(BottomWidgetType::Temp),
|
||||
"disk" => Ok(BottomWidgetType::Disk),
|
||||
"empty" => Ok(BottomWidgetType::Empty),
|
||||
"battery" | "batt" => Ok(BottomWidgetType::Battery),
|
||||
_ => Err(BottomError::ConfigError(format!(
|
||||
"Invalid widget type: {}",
|
||||
s
|
||||
|
@ -946,4 +948,5 @@ pub struct UsedWidgets {
|
|||
pub use_proc: bool,
|
||||
pub use_disk: bool,
|
||||
pub use_temp: bool,
|
||||
pub use_battery: bool,
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ use crate::{
|
|||
App,
|
||||
},
|
||||
constants::*,
|
||||
data_conversion::{ConvertedCpuData, ConvertedProcessData},
|
||||
data_conversion::{ConvertedBatteryData, ConvertedCpuData, ConvertedProcessData},
|
||||
utils::error,
|
||||
};
|
||||
|
||||
|
@ -51,6 +51,7 @@ pub struct DisplayableData {
|
|||
pub mem_data: Vec<(f64, f64)>,
|
||||
pub swap_data: Vec<(f64, f64)>,
|
||||
pub cpu_data: Vec<ConvertedCpuData>,
|
||||
pub battery_data: Vec<ConvertedBatteryData>,
|
||||
}
|
||||
|
||||
/// Handles the canvas' state. TODO: [OPT] implement this.
|
||||
|
@ -201,20 +202,6 @@ impl Painter {
|
|||
}
|
||||
}
|
||||
|
||||
// pub fn draw_specific_table<B: Backend>(
|
||||
// &self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect, draw_border: bool,
|
||||
// widget_selected: WidgetPosition,
|
||||
// ) {
|
||||
// match widget_selected {
|
||||
// WidgetPosition::Process | WidgetPosition::ProcessSearch => {
|
||||
// self.draw_process_and_search(f, app_state, draw_loc, draw_border)
|
||||
// }
|
||||
// WidgetPosition::Temp => self.draw_temp_table(f, app_state, draw_loc, draw_border),
|
||||
// WidgetPosition::Disk => self.draw_disk_table(f, app_state, draw_loc, draw_border),
|
||||
// _ => {}
|
||||
// }
|
||||
// }
|
||||
|
||||
// TODO: [FEATURE] Auto-resizing dialog sizes.
|
||||
pub fn draw_data<B: Backend>(
|
||||
&mut self, terminal: &mut Terminal<B>, app_state: &mut app::App,
|
||||
|
@ -375,6 +362,12 @@ impl Painter {
|
|||
true,
|
||||
app_state.current_widget.widget_id - 1,
|
||||
),
|
||||
Battery => self.draw_battery_display(
|
||||
&mut f,
|
||||
app_state,
|
||||
rect[0],
|
||||
app_state.current_widget.widget_id,
|
||||
),
|
||||
_ => {}
|
||||
}
|
||||
} else if app_state.app_config_fields.use_basic_mode {
|
||||
|
@ -564,6 +557,9 @@ impl Painter {
|
|||
true,
|
||||
widget.widget_id,
|
||||
),
|
||||
Battery => {
|
||||
self.draw_battery_display(f, app_state, *widget_draw_loc, widget.widget_id)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ pub struct CanvasColours {
|
|||
pub text_style: Style,
|
||||
pub widget_title_style: Style,
|
||||
pub graph_style: Style,
|
||||
// Full, Medium, Low
|
||||
pub battery_bar_styles: Vec<Style>,
|
||||
}
|
||||
|
||||
impl Default for CanvasColours {
|
||||
|
@ -48,6 +50,14 @@ impl Default for CanvasColours {
|
|||
text_style: Style::default().fg(text_colour),
|
||||
widget_title_style: Style::default().fg(text_colour),
|
||||
graph_style: Style::default().fg(text_colour),
|
||||
battery_bar_styles: vec![
|
||||
Style::default().fg(Color::Red),
|
||||
Style::default().fg(Color::Yellow),
|
||||
Style::default().fg(Color::Yellow),
|
||||
Style::default().fg(Color::Green),
|
||||
Style::default().fg(Color::Green),
|
||||
Style::default().fg(Color::Green),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,4 +160,20 @@ impl CanvasColours {
|
|||
self.graph_style = get_style_from_config(colour)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_battery_colours(&mut self, colours: &[String]) -> error::Result<()> {
|
||||
if colours.is_empty() {
|
||||
Err(error::BottomError::ConfigError(
|
||||
"Battery colour list must have at least one colour!".to_string(),
|
||||
))
|
||||
} else {
|
||||
let generated_colours: Result<Vec<_>, _> = colours
|
||||
.iter()
|
||||
.map(|colour| get_style_from_config(colour))
|
||||
.collect();
|
||||
|
||||
self.battery_bar_styles = generated_colours?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -186,7 +186,7 @@ fn convert_name_to_color(color_name: &str) -> error::Result<Color> {
|
|||
Err(error::BottomError::GenericError(format!(
|
||||
"Color {} is not a supported config colour. bottom supports the following named colours as strings: \
|
||||
Reset, Black, Red, Green, Yellow, Blue, Magenta, Cyan, Gray, DarkGray, LightRed, LightGreen, \
|
||||
LightYellow, LightBlue, LightMagenta, LightCyan, White",
|
||||
LightYellow, LightBlue, LightMagenta, LightCyan, White",
|
||||
color_name
|
||||
)))
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
pub mod basic_table_arrows;
|
||||
pub mod battery_display;
|
||||
pub mod cpu_basic;
|
||||
pub mod cpu_graph;
|
||||
pub mod disk_table;
|
||||
|
@ -10,6 +11,7 @@ pub mod process_table;
|
|||
pub mod temp_table;
|
||||
|
||||
pub use basic_table_arrows::BasicTableArrows;
|
||||
pub use battery_display::BatteryDisplayWidget;
|
||||
pub use cpu_basic::CpuBasicWidget;
|
||||
pub use cpu_graph::CpuGraphWidget;
|
||||
pub use disk_table::DiskTableWidget;
|
||||
|
|
145
src/canvas/widgets/battery_display.rs
Normal file
145
src/canvas/widgets/battery_display.rs
Normal file
|
@ -0,0 +1,145 @@
|
|||
use std::cmp::max;
|
||||
|
||||
use crate::{
|
||||
app::App,
|
||||
canvas::{drawing_utils::calculate_basic_use_bars, Painter},
|
||||
};
|
||||
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{Constraint, Rect},
|
||||
terminal::Frame,
|
||||
widgets::{Block, Borders, Paragraph, Row, Table, Tabs, Text, Widget},
|
||||
};
|
||||
|
||||
pub trait BatteryDisplayWidget {
|
||||
fn draw_battery_display<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
);
|
||||
}
|
||||
|
||||
impl BatteryDisplayWidget for Painter {
|
||||
fn draw_battery_display<B: Backend>(
|
||||
&self, f: &mut Frame<'_, B>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
|
||||
) {
|
||||
if let Some(battery_widget_state) =
|
||||
app_state.battery_state.widget_states.get_mut(&widget_id)
|
||||
{
|
||||
let title = if app_state.is_expanded {
|
||||
const TITLE_BASE: &str = " Battery ── Esc to go back ";
|
||||
let repeat_num = max(
|
||||
0,
|
||||
draw_loc.width as i32 - TITLE_BASE.chars().count() as i32 - 2,
|
||||
);
|
||||
let result_title = format!(
|
||||
" Battery ─{}─ Esc to go back ",
|
||||
"─".repeat(repeat_num as usize)
|
||||
);
|
||||
result_title
|
||||
} else {
|
||||
" Battery ".to_string()
|
||||
};
|
||||
|
||||
let border_and_title_style = if app_state.current_widget.widget_id == widget_id {
|
||||
self.colours.highlighted_border_style
|
||||
} else {
|
||||
self.colours.border_style
|
||||
};
|
||||
|
||||
let battery_block = Block::default()
|
||||
.title(&title)
|
||||
.title_style(if app_state.is_expanded {
|
||||
border_and_title_style
|
||||
} else {
|
||||
self.colours.widget_title_style
|
||||
})
|
||||
.borders(Borders::ALL)
|
||||
.border_style(border_and_title_style);
|
||||
|
||||
if let Some(battery_details) = app_state
|
||||
.canvas_data
|
||||
.battery_data
|
||||
.get(battery_widget_state.currently_selected_battery_index)
|
||||
{
|
||||
// Assuming a 50/50 split in width
|
||||
let bar_length = max(0, (draw_loc.width as i64 - 2) / 2 - 8) as usize;
|
||||
let charge_percentage = battery_details.charge_percentage;
|
||||
let num_bars = calculate_basic_use_bars(charge_percentage, bar_length);
|
||||
let bars = format!(
|
||||
"[{}{}{:3.0}%]",
|
||||
"|".repeat(num_bars),
|
||||
" ".repeat(bar_length - num_bars),
|
||||
charge_percentage,
|
||||
);
|
||||
|
||||
let battery_items = vec![
|
||||
["Charge %", &bars],
|
||||
["Consumption", &battery_details.watt_consumption],
|
||||
if let Some(duration_until_full) = &battery_details.duration_until_full {
|
||||
["Time to full", duration_until_full]
|
||||
} else if let Some(duration_until_empty) = &battery_details.duration_until_empty
|
||||
{
|
||||
["Time to empty", duration_until_empty]
|
||||
} else {
|
||||
["Time to full/empty", "N/A"]
|
||||
},
|
||||
];
|
||||
|
||||
let battery_rows = battery_items.iter().enumerate().map(|(itx, item)| {
|
||||
Row::StyledData(
|
||||
item.iter(),
|
||||
if itx == 0 {
|
||||
let colour_index = ((charge_percentage
|
||||
* self.colours.battery_bar_styles.len() as f64
|
||||
- 1.0)
|
||||
/ 100.0)
|
||||
.floor() as usize;
|
||||
*self
|
||||
.colours
|
||||
.battery_bar_styles
|
||||
.get(colour_index)
|
||||
.unwrap_or(&self.colours.text_style)
|
||||
} else {
|
||||
self.colours.text_style
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
// Draw
|
||||
Table::new([""].iter(), battery_rows)
|
||||
.block(battery_block)
|
||||
.header_style(self.colours.table_header_style)
|
||||
.widths([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
||||
.render(f, draw_loc);
|
||||
} else {
|
||||
Paragraph::new(
|
||||
[Text::Styled(
|
||||
"No data found for this battery".into(),
|
||||
self.colours.text_style,
|
||||
)]
|
||||
.iter(),
|
||||
)
|
||||
.block(battery_block)
|
||||
.render(f, draw_loc);
|
||||
}
|
||||
// if app_state.canvas_data.battery_data.len() > 1 {
|
||||
Tabs::default()
|
||||
.block(battery_block)
|
||||
.titles(
|
||||
(app_state
|
||||
.canvas_data
|
||||
.battery_data
|
||||
.iter()
|
||||
.map(|battery| &battery.battery_name))
|
||||
.collect::<Vec<_>>()
|
||||
.as_ref(),
|
||||
)
|
||||
.divider(tui::symbols::line::VERTICAL)
|
||||
.style(self.colours.text_style)
|
||||
.highlight_style(self.colours.currently_selected_text_style)
|
||||
.select(battery_widget_state.currently_selected_battery_index)
|
||||
.render(f, draw_loc);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ use tui::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
app::{self},
|
||||
app,
|
||||
canvas::{
|
||||
drawing_utils::{get_start_position, get_variable_intrinsic_widths},
|
||||
Painter,
|
||||
|
|
|
@ -199,6 +199,9 @@ pub const DEFAULT_CONFIG_CONTENT: &str = r##"
|
|||
# Represents the colour of the lines and text of the graph.
|
||||
#graph_color="Gray"
|
||||
|
||||
# Represents the colours of the battery based on charge
|
||||
#battery_colors = ["red", "yellow", "yellow", "green", "green", "green"]
|
||||
|
||||
##########################################################
|
||||
|
||||
# Layout - layouts follow a pattern like this:
|
||||
|
|
|
@ -14,6 +14,15 @@ use crate::{
|
|||
|
||||
type Point = (f64, f64);
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ConvertedBatteryData {
|
||||
pub battery_name: String,
|
||||
pub charge_percentage: f64,
|
||||
pub watt_consumption: String,
|
||||
pub duration_until_full: Option<String>,
|
||||
pub duration_until_empty: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ConvertedNetworkData {
|
||||
pub rx: Vec<Point>,
|
||||
|
@ -413,3 +422,50 @@ pub fn convert_process_data(
|
|||
|
||||
(single_list, grouped_list)
|
||||
}
|
||||
|
||||
pub fn convert_battery_harvest(
|
||||
current_data: &data_farmer::DataCollection,
|
||||
) -> Vec<ConvertedBatteryData> {
|
||||
current_data
|
||||
.battery_harvest
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(itx, battery_harvest)| ConvertedBatteryData {
|
||||
battery_name: format!("Battery {}", itx),
|
||||
charge_percentage: battery_harvest.charge_percent,
|
||||
watt_consumption: format!("{:.2}W", battery_harvest.power_consumption_rate_watts),
|
||||
duration_until_empty: if let Some(secs_till_empty) = battery_harvest.secs_until_empty {
|
||||
let time = chrono::Duration::seconds(secs_till_empty);
|
||||
let num_minutes = time.num_minutes() - time.num_hours() * 60;
|
||||
let num_seconds = time.num_seconds() - time.num_minutes() * 60;
|
||||
Some(format!(
|
||||
"{} hour{}, {} minute{}, {} second{}",
|
||||
time.num_hours(),
|
||||
if time.num_hours() == 1 { "" } else { "s" },
|
||||
num_minutes,
|
||||
if num_minutes == 1 { "" } else { "s" },
|
||||
num_seconds,
|
||||
if num_seconds == 1 { "" } else { "s" },
|
||||
))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
duration_until_full: if let Some(secs_till_full) = battery_harvest.secs_until_full {
|
||||
let time = chrono::Duration::seconds(secs_till_full);
|
||||
let num_minutes = time.num_minutes() - time.num_hours() * 60;
|
||||
let num_seconds = time.num_seconds() - time.num_minutes() * 60;
|
||||
Some(format!(
|
||||
"{} hour{}, {} minute{}, {} second{}",
|
||||
time.num_hours(),
|
||||
if time.num_hours() == 1 { "" } else { "s" },
|
||||
num_minutes,
|
||||
if num_minutes == 1 { "" } else { "s" },
|
||||
num_seconds,
|
||||
if num_seconds == 1 { "" } else { "s" },
|
||||
))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -228,6 +228,12 @@ fn main() -> error::Result<()> {
|
|||
app.canvas_data.grouped_process_data = grouped;
|
||||
update_all_process_lists(&mut app);
|
||||
}
|
||||
|
||||
// Battery
|
||||
if app.used_widgets.use_battery {
|
||||
app.canvas_data.battery_data =
|
||||
convert_battery_harvest(&app.data_collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
BottomEvent::Clean => {
|
||||
|
@ -491,6 +497,10 @@ fn generate_config_colours(config: &Config, painter: &mut canvas::Painter) -> er
|
|||
if let Some(graph_color) = &colours.graph_color {
|
||||
painter.colours.set_graph_colour(graph_color)?;
|
||||
}
|
||||
|
||||
if let Some(battery_colors) = &colours.battery_colors {
|
||||
painter.colours.set_battery_colours(battery_colors)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -3,11 +3,7 @@ use std::collections::{HashMap, HashSet};
|
|||
use std::time::Instant;
|
||||
|
||||
use crate::{
|
||||
app::{
|
||||
data_harvester, layout_manager::*, App, AppConfigFields, BasicTableWidgetState, CpuState,
|
||||
CpuWidgetState, DiskState, DiskWidgetState, MemState, MemWidgetState, NetState,
|
||||
NetWidgetState, ProcState, ProcWidgetState, TempState, TempWidgetState,
|
||||
},
|
||||
app::{layout_manager::*, *},
|
||||
constants::*,
|
||||
utils::error::{self, BottomError},
|
||||
};
|
||||
|
@ -65,12 +61,14 @@ pub struct ConfigColours {
|
|||
pub selected_bg_color: Option<String>,
|
||||
pub widget_title_color: Option<String>,
|
||||
pub graph_color: Option<String>,
|
||||
pub battery_colors: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
pub fn build_app(
|
||||
matches: &clap::ArgMatches<'static>, config: &Config, widget_layout: &BottomLayout,
|
||||
default_widget_id: u64,
|
||||
) -> error::Result<App> {
|
||||
use BottomWidgetType::*;
|
||||
let autohide_time = get_autohide_time(&matches, &config);
|
||||
let default_time_value = get_default_time_value(&matches, &config)?;
|
||||
let use_basic_mode = get_use_basic_mode(&matches, &config);
|
||||
|
@ -88,6 +86,7 @@ pub fn build_app(
|
|||
let mut proc_state_map: HashMap<u64, ProcWidgetState> = HashMap::new();
|
||||
let mut temp_state_map: HashMap<u64, TempWidgetState> = HashMap::new();
|
||||
let mut disk_state_map: HashMap<u64, DiskWidgetState> = HashMap::new();
|
||||
let mut battery_state_map: HashMap<u64, BatteryWidgetState> = HashMap::new();
|
||||
|
||||
let autohide_timer = if autohide_time {
|
||||
Some(Instant::now())
|
||||
|
@ -97,9 +96,8 @@ pub fn build_app(
|
|||
|
||||
let (default_widget_type_option, _) = get_default_widget_and_count(matches, config)?;
|
||||
let mut initial_widget_id: u64 = default_widget_id;
|
||||
let mut initial_widget_type = BottomWidgetType::Proc;
|
||||
let mut initial_widget_type = Proc;
|
||||
let is_custom_layout = config.row.is_some();
|
||||
|
||||
let mut used_widget_set = HashSet::new();
|
||||
|
||||
for row in &widget_layout.rows {
|
||||
|
@ -110,22 +108,22 @@ pub fn build_app(
|
|||
if let Some(default_widget_type) = &default_widget_type_option {
|
||||
if !is_custom_layout || use_basic_mode {
|
||||
match widget.widget_type {
|
||||
BottomWidgetType::BasicCpu => {
|
||||
if let BottomWidgetType::Cpu = *default_widget_type {
|
||||
BasicCpu => {
|
||||
if let Cpu = *default_widget_type {
|
||||
initial_widget_id = widget.widget_id;
|
||||
initial_widget_type = BottomWidgetType::Cpu;
|
||||
initial_widget_type = Cpu;
|
||||
}
|
||||
}
|
||||
BottomWidgetType::BasicMem => {
|
||||
if let BottomWidgetType::Mem = *default_widget_type {
|
||||
BasicMem => {
|
||||
if let Mem = *default_widget_type {
|
||||
initial_widget_id = widget.widget_id;
|
||||
initial_widget_type = BottomWidgetType::Cpu;
|
||||
initial_widget_type = Cpu;
|
||||
}
|
||||
}
|
||||
BottomWidgetType::BasicNet => {
|
||||
if let BottomWidgetType::Net = *default_widget_type {
|
||||
BasicNet => {
|
||||
if let Net = *default_widget_type {
|
||||
initial_widget_id = widget.widget_id;
|
||||
initial_widget_type = BottomWidgetType::Cpu;
|
||||
initial_widget_type = Cpu;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
@ -141,25 +139,25 @@ pub fn build_app(
|
|||
used_widget_set.insert(widget.widget_type.clone());
|
||||
|
||||
match widget.widget_type {
|
||||
BottomWidgetType::Cpu => {
|
||||
Cpu => {
|
||||
cpu_state_map.insert(
|
||||
widget.widget_id,
|
||||
CpuWidgetState::init(default_time_value, autohide_timer),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::Mem => {
|
||||
Mem => {
|
||||
mem_state_map.insert(
|
||||
widget.widget_id,
|
||||
MemWidgetState::init(default_time_value, autohide_timer),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::Net => {
|
||||
Net => {
|
||||
net_state_map.insert(
|
||||
widget.widget_id,
|
||||
NetWidgetState::init(default_time_value, autohide_timer),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::Proc => {
|
||||
Proc => {
|
||||
proc_state_map.insert(
|
||||
widget.widget_id,
|
||||
ProcWidgetState::init(
|
||||
|
@ -170,13 +168,18 @@ pub fn build_app(
|
|||
),
|
||||
);
|
||||
}
|
||||
BottomWidgetType::Disk => {
|
||||
Disk => {
|
||||
disk_state_map.insert(widget.widget_id, DiskWidgetState::init());
|
||||
}
|
||||
BottomWidgetType::Temp => {
|
||||
Temp => {
|
||||
temp_state_map.insert(widget.widget_id, TempWidgetState::init());
|
||||
}
|
||||
_ => {}
|
||||
Battery => {
|
||||
battery_state_map
|
||||
.insert(widget.widget_id, BatteryWidgetState::default());
|
||||
}
|
||||
Empty | BasicCpu | BasicMem | BasicNet | BasicTables | ProcSearch
|
||||
| CpuLegend => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -185,15 +188,13 @@ pub fn build_app(
|
|||
|
||||
let basic_table_widget_state = if use_basic_mode {
|
||||
Some(match initial_widget_type {
|
||||
BottomWidgetType::Proc | BottomWidgetType::Disk | BottomWidgetType::Temp => {
|
||||
BasicTableWidgetState {
|
||||
currently_displayed_widget_type: initial_widget_type,
|
||||
currently_displayed_widget_id: initial_widget_id,
|
||||
widget_id: 100,
|
||||
}
|
||||
}
|
||||
Proc | Disk | Temp => BasicTableWidgetState {
|
||||
currently_displayed_widget_type: initial_widget_type,
|
||||
currently_displayed_widget_id: initial_widget_id,
|
||||
widget_id: 100,
|
||||
},
|
||||
_ => BasicTableWidgetState {
|
||||
currently_displayed_widget_type: BottomWidgetType::Proc,
|
||||
currently_displayed_widget_type: Proc,
|
||||
currently_displayed_widget_id: DEFAULT_WIDGET_ID,
|
||||
widget_id: 100,
|
||||
},
|
||||
|
@ -218,15 +219,13 @@ pub fn build_app(
|
|||
};
|
||||
|
||||
let used_widgets = UsedWidgets {
|
||||
use_cpu: used_widget_set.get(&BottomWidgetType::Cpu).is_some()
|
||||
|| used_widget_set.get(&BottomWidgetType::BasicCpu).is_some(),
|
||||
use_mem: used_widget_set.get(&BottomWidgetType::Mem).is_some()
|
||||
|| used_widget_set.get(&BottomWidgetType::BasicMem).is_some(),
|
||||
use_net: used_widget_set.get(&BottomWidgetType::Net).is_some()
|
||||
|| used_widget_set.get(&BottomWidgetType::BasicNet).is_some(),
|
||||
use_proc: used_widget_set.get(&BottomWidgetType::Proc).is_some(),
|
||||
use_disk: used_widget_set.get(&BottomWidgetType::Disk).is_some(),
|
||||
use_temp: used_widget_set.get(&BottomWidgetType::Temp).is_some(),
|
||||
use_cpu: used_widget_set.get(&Cpu).is_some() || used_widget_set.get(&BasicCpu).is_some(),
|
||||
use_mem: used_widget_set.get(&Mem).is_some() || used_widget_set.get(&BasicMem).is_some(),
|
||||
use_net: used_widget_set.get(&Net).is_some() || used_widget_set.get(&BasicNet).is_some(),
|
||||
use_proc: used_widget_set.get(&Proc).is_some(),
|
||||
use_disk: used_widget_set.get(&Disk).is_some(),
|
||||
use_temp: used_widget_set.get(&Temp).is_some(),
|
||||
use_battery: used_widget_set.get(&Battery).is_some(),
|
||||
};
|
||||
|
||||
Ok(App::builder()
|
||||
|
@ -237,6 +236,7 @@ pub fn build_app(
|
|||
.proc_state(ProcState::init(proc_state_map))
|
||||
.disk_state(DiskState::init(disk_state_map))
|
||||
.temp_state(TempState::init(temp_state_map))
|
||||
.battery_state(BatteryState::init(battery_state_map))
|
||||
.basic_table_widget_state(basic_table_widget_state)
|
||||
.current_widget(widget_map.get(&initial_widget_id).unwrap().clone()) // I think the unwrap is fine here
|
||||
.widget_map(widget_map)
|
||||
|
@ -275,9 +275,16 @@ pub fn get_widget_layout(
|
|||
.collect::<error::Result<Vec<_>>>()?,
|
||||
total_row_height_ratio: total_height_ratio,
|
||||
};
|
||||
ret_bottom_layout.get_movement_mappings();
|
||||
|
||||
ret_bottom_layout
|
||||
// Confirm that we have at least ONE widget - if we don't, go back to default!
|
||||
if iter_id > 0 {
|
||||
ret_bottom_layout.get_movement_mappings();
|
||||
ret_bottom_layout
|
||||
} else {
|
||||
return Err(error::BottomError::ConfigError(
|
||||
"Invalid layout - please have at least one widget.".to_string(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
default_widget_id = DEFAULT_WIDGET_ID;
|
||||
BottomLayout::init_default(left_legend)
|
||||
|
|
Loading…
Reference in a new issue