From f5f7f191816e61fe043aaa670529d39280c34a2d Mon Sep 17 00:00:00 2001 From: jer Date: Tue, 20 Jan 2026 12:26:10 +1100 Subject: [PATCH 01/10] wip: 'f' and '/' to find a specific process to track --- Cargo.lock | 1635 +++++++++++++++++++++++++++++++++++++++++++--------- Cargo.toml | 12 +- src/app.rs | 165 +++++- src/gpu.rs | 2 +- 4 files changed, 1504 insertions(+), 310 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7080cd1..3c6432c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,66 +1,113 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", + "once_cell_polyfill", "windows-sys", ] [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "atomic" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89cbf775b137e9b968e67227ef7f775587cde3fd31b0d8599dbd0f598a48340" +dependencies = [ + "bytemuck", +] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bit-set" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" @@ -70,27 +117,57 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] -name = "cassowary" -version = "0.3.0" +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytemuck" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" + +[[package]] +name = "castaway" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" +dependencies = [ + "rustversion", +] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clap" -version = "4.4.6" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", "clap_derive", @@ -98,51 +175,85 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.6" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.10.0", + "strsim", ] [[package]] name = "clap_derive" -version = "4.4.2" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.114", ] [[package]] name = "clap_lex" -version = "0.5.1" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "compact_str" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "rustversion", + "ryu", + "static_assertions", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] [[package]] name = "crossterm" -version = "0.27.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.10.0", "crossterm_winapi", - "libc", + "derive_more", + "document-features", "mio", "parking_lot", + "rustix", "signal-hook", "signal-hook-mio", "winapi", @@ -157,488 +268,1436 @@ dependencies = [ "winapi", ] +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "csscolorparser" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2a7d3066da2de787b7f032c736763eb7ae5d355f81a68bab2675a96008b0bf" +dependencies = [ + "lab", + "phf", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + [[package]] name = "darling" -version = "0.10.2" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.23.0", + "darling_macro 0.23.0", ] [[package]] name = "darling_core" -version = "0.10.2" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.9.3", - "syn 1.0.109", + "strsim", + "syn 2.0.114", ] [[package]] -name = "darling_macro" -version = "0.10.2" +name = "darling_core" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ - "darling_core", + "ident_case", + "proc-macro2", "quote", - "syn 1.0.109", + "strsim", + "syn 2.0.114", ] [[package]] -name = "either" -version = "1.9.0" +name = "darling_macro" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.114", +] [[package]] -name = "fnv" -version = "1.0.7" +name = "darling_macro" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.114", +] [[package]] -name = "heck" -version = "0.4.1" +name = "deltae" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" [[package]] -name = "ident_case" -version = "1.0.1" +name = "deranged" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] [[package]] -name = "indoc" -version = "2.0.4" +name = "derive_more" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] [[package]] -name = "itertools" -version = "0.11.0" +name = "derive_more-impl" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "either", + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.114", ] [[package]] -name = "libc" -version = "0.2.149" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] [[package]] -name = "libloading" -version = "0.7.4" +name = "document-features" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ - "cfg-if", - "winapi", + "litrs", ] [[package]] -name = "lock_api" -version = "0.4.10" +name = "either" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] -name = "log" -version = "0.4.20" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "mio" -version = "0.8.8" +name = "errno" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "log", - "wasi", "windows-sys", ] [[package]] -name = "nvml-wrapper" -version = "0.9.0" +name = "euclid" +version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd21b9f5a1cce3c3515c9ffa85f5c7443e07162dae0ccf4339bb7ca38ad3454" +checksum = "df61bf483e837f88d5c2291dcf55c67be7e676b3a51acc48db3a7b163b91ed63" dependencies = [ - "bitflags 1.3.2", - "libloading", - "nvml-wrapper-sys", - "static_assertions", - "thiserror", - "wrapcenum-derive", + "num-traits", ] [[package]] -name = "nvml-wrapper-sys" -version = "0.7.0" +name = "fancy-regex" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c961a2ea9e91c59a69b78e69090f6f5b867bb46c0c56de9482da232437c4987e" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" dependencies = [ - "libloading", + "bit-set", + "regex", ] [[package]] -name = "nvtop" -version = "0.1.2" +name = "filedescriptor" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" dependencies = [ - "anyhow", - "clap", - "crossterm", - "nvml-wrapper", - "ratatui", - "thiserror", + "libc", + "thiserror 1.0.69", + "winapi", ] [[package]] -name = "parking_lot" -version = "0.12.1" +name = "finl_unicode" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] +checksum = "9844ddc3a6e533d62bba727eb6c28b5d360921d5175e9ff0f1e621a5c590a4d5" [[package]] -name = "parking_lot_core" -version = "0.9.8" +name = "fixedbitset" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets", -] +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] -name = "paste" -version = "1.0.14" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "proc-macro2" -version = "1.0.68" +name = "foldhash" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1106fec09662ec6dd98ccac0f81cef56984d0b49f75c92d8cbad76e20c005c" -dependencies = [ - "unicode-ident", -] +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] -name = "quote" -version = "1.0.33" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "proc-macro2", + "typenum", + "version_check", ] [[package]] -name = "ratatui" -version = "0.23.0" +name = "getrandom" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e2e4cd95294a85c3b4446e63ef054eea43e0205b1fd60120c16b74ff7ff96ad" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ - "bitflags 2.4.0", - "cassowary", - "crossterm", - "indoc", - "itertools", - "paste", - "strum", - "unicode-segmentation", - "unicode-width", + "cfg-if", + "libc", + "r-efi", + "wasip2", ] [[package]] -name = "redox_syscall" -version = "0.3.5" +name = "hashbrown" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ - "bitflags 1.3.2", + "allocator-api2", + "equivalent", + "foldhash", ] [[package]] -name = "rustversion" -version = "1.0.14" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "scopeguard" -version = "1.2.0" +name = "hex" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "signal-hook" -version = "0.3.17" +name = "ident_case" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "signal-hook-mio" -version = "0.2.3" +name = "indoc" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" dependencies = [ - "libc", - "mio", - "signal-hook", + "rustversion", ] [[package]] -name = "signal-hook-registry" -version = "1.4.1" +name = "instability" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" dependencies = [ - "libc", + "darling 0.23.0", + "indoc", + "proc-macro2", + "quote", + "syn 2.0.114", ] [[package]] -name = "smallvec" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" - -[[package]] -name = "static_assertions" -version = "1.1.0" +name = "is_terminal_polyfill" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] -name = "strsim" -version = "0.9.3" +name = "itertools" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] [[package]] -name = "strsim" -version = "0.10.0" +name = "itoa" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] -name = "strum" -version = "0.25.0" +name = "js-sys" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ - "strum_macros", + "once_cell", + "wasm-bindgen", ] [[package]] -name = "strum_macros" -version = "0.25.2" +name = "kasuari" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" +checksum = "8fe90c1150662e858c7d5f945089b7517b0a80d8bf7ba4b1b5ffc984e7230a5b" dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.38", + "hashbrown", + "portable-atomic", + "thiserror 2.0.18", ] [[package]] -name = "syn" -version = "1.0.109" +name = "lab" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] +checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" [[package]] -name = "syn" -version = "2.0.38" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" -dependencies = [ +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "line-clipping" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4de44e98ddbf09375cbf4d17714d18f39195f4f4894e8524501726fd9a8a4a" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "mac_address" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" +dependencies = [ + "nix", + "winapi", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "memmem" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.10.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "nvml-wrapper" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5c6c0ef9702176a570f06ad94f3198bc29c524c8b498f1b9346e1b1bdcbb3a" +dependencies = [ + "bitflags 2.10.0", + "libloading", + "nvml-wrapper-sys", + "static_assertions", + "thiserror 1.0.69", + "wrapcenum-derive", +] + +[[package]] +name = "nvml-wrapper-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd23dbe2eb8d8335d2bce0299e0a07d6a63c089243d626ca75b770a962ff49e6" +dependencies = [ + "libloading", +] + +[[package]] +name = "nvtop" +version = "0.1.2" +dependencies = [ + "anyhow", + "clap", + "crossterm", + "nvml-wrapper", + "ratatui", + "thiserror 2.0.18", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pest" +version = "2.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "pest_meta" +version = "2.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" +dependencies = [ + "pest", + "sha2", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "portable-atomic" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "ratatui" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1ce67fb8ba4446454d1c8dbaeda0557ff5e94d39d5e5ed7f10a65eb4c8266bc" +dependencies = [ + "instability", + "ratatui-core", + "ratatui-crossterm", + "ratatui-macros", + "ratatui-termwiz", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" +dependencies = [ + "bitflags 2.10.0", + "compact_str", + "hashbrown", + "indoc", + "itertools", + "kasuari", + "lru", + "strum", + "thiserror 2.0.18", + "unicode-segmentation", + "unicode-truncate", + "unicode-width", +] + +[[package]] +name = "ratatui-crossterm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "577c9b9f652b4c121fb25c6a391dd06406d3b092ba68827e6d2f09550edc54b3" +dependencies = [ + "cfg-if", + "crossterm", + "instability", + "ratatui-core", +] + +[[package]] +name = "ratatui-macros" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f1342a13e83e4bb9d0b793d0ea762be633f9582048c892ae9041ef39c936f4" +dependencies = [ + "ratatui-core", + "ratatui-widgets", +] + +[[package]] +name = "ratatui-termwiz" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f76fe0bd0ed4295f0321b1676732e2454024c15a35d01904ddb315afd3d545c" +dependencies = [ + "ratatui-core", + "termwiz", +] + +[[package]] +name = "ratatui-widgets" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" +dependencies = [ + "bitflags 2.10.0", + "hashbrown", + "indoc", + "instability", + "itertools", + "line-clipping", + "ratatui-core", + "strum", + "time", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags 2.10.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "terminfo" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ea810f0692f9f51b382fff5893887bb4580f5fa246fde546e0b13e7fcee662" +dependencies = [ + "fnv", + "nom", + "phf", + "phf_codegen", +] + +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] + +[[package]] +name = "termwiz" +version = "0.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" +dependencies = [ + "anyhow", + "base64", + "bitflags 2.10.0", + "fancy-regex", + "filedescriptor", + "finl_unicode", + "fixedbitset", + "hex", + "lazy_static", + "libc", + "log", + "memmem", + "nix", + "num-derive", + "num-traits", + "ordered-float", + "pest", + "pest_derive", + "phf", + "sha2", + "signal-hook", + "siphasher", + "terminfo", + "termios", + "thiserror 1.0.69", + "ucd-trie", + "unicode-segmentation", + "vtparse", + "wezterm-bidi", + "wezterm-blob-leases", + "wezterm-color-types", + "wezterm-dynamic", + "wezterm-input-types", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" -version = "1.0.49" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn 2.0.114", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.38", + "syn 2.0.114", +] + +[[package]] +name = "time" +version = "0.3.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" +dependencies = [ + "deranged", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", ] +[[package]] +name = "time-core" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-truncate" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5" +dependencies = [ + "itertools", + "unicode-segmentation", + "unicode-width", +] [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +dependencies = [ + "atomic", + "getrandom", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vtparse" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9b2acfb050df409c972a37d3b8e08cdea3bddb0c09db9d53137e504cfabed0" +dependencies = [ + "utf8parse", +] [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "winapi" -version = "0.3.9" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "wit-bindgen", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "wasm-bindgen" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "wasm-bindgen-macro" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "wasm-bindgen-macro-support" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.114", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wezterm-bidi" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0a6e355560527dd2d1cf7890652f4f09bb3433b6aadade4c9b5ed76de5f3ec" +dependencies = [ + "log", + "wezterm-dynamic", +] + +[[package]] +name = "wezterm-blob-leases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692daff6d93d94e29e4114544ef6d5c942a7ed998b37abdc19b17136ea428eb7" +dependencies = [ + "getrandom", + "mac_address", + "sha2", + "thiserror 1.0.69", + "uuid", +] + +[[package]] +name = "wezterm-color-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de81ef35c9010270d63772bebef2f2d6d1f2d20a983d27505ac850b8c4b4296" +dependencies = [ + "csscolorparser", + "deltae", + "lazy_static", + "wezterm-dynamic", +] + +[[package]] +name = "wezterm-dynamic" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "5f2ab60e120fd6eaa68d9567f3226e876684639d22a4219b313ff69ec0ccd5ac" dependencies = [ - "windows-targets", + "log", + "ordered-float", + "strsim", + "thiserror 1.0.69", + "wezterm-dynamic-derive", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "wezterm-dynamic-derive" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "46c0cf2d539c645b448eaffec9ec494b8b19bd5077d9e58cb1ae7efece8d575b" 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", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" +name = "wezterm-input-types" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "7012add459f951456ec9d6c7e6fc340b1ce15d6fc9629f8c42853412c029e57e" +dependencies = [ + "bitflags 1.3.2", + "euclid", + "lazy_static", + "serde", + "wezterm-dynamic", +] [[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +name = "winapi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] [[package]] -name = "windows_i686_gnu" -version = "0.48.5" +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] -name = "windows_i686_msvc" -version = "0.48.5" +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +name = "wit-bindgen" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "wrapcenum-derive" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bcc065c85ad2c3bd12aa4118bf164835712e25080c392557801a13292c60aec" +checksum = "a76ff259533532054cfbaefb115c613203c73707017459206380f03b3b3f266e" dependencies = [ - "darling", + "darling 0.20.11", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.114", ] diff --git a/Cargo.toml b/Cargo.toml index 1475e49..b9c1623 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,9 +18,9 @@ homepage = "https://www.jeremyfwebb.ninja" publish = true [dependencies] -anyhow = "1.0.75" -clap = { version = "4.4.6", features = ["derive"] } -crossterm = "0.27.0" -nvml-wrapper = "0.9.0" -ratatui = "0.23.0" -thiserror = "1.0.49" +anyhow = "1.0.100" +clap = { version = "4.5.54", features = ["derive"] } +crossterm = "0.29.0" +nvml-wrapper = "0.11.0" +ratatui = "0.30.0" +thiserror = "2.0.18" diff --git a/src/app.rs b/src/app.rs index 6a9c7fa..e20f14e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -8,6 +8,7 @@ use ratatui::{prelude::*, widgets::*}; use ratatui::{ prelude::{CrosstermBackend, Terminal}, widgets::Paragraph, + widgets::Block, }; use std::time::Duration; @@ -16,7 +17,6 @@ use crate::errors::NvTopError; use crate::stylers::calculate_severity; use crate::termite::LoggingHandle; use crate::{errors, gpu::GpuInfo}; -pub type Frame<'a> = ratatui::Frame<'a, CrosstermBackend>; pub fn run( nvml: nvml_wrapper::Nvml, @@ -35,6 +35,9 @@ pub fn run( let mut have_fans: bool = gpu_list .iter() .any(|gpu| gpu.inner.num_fans().map_or(0, |fc| fc) != 0); + + // State variables for process view + let mut show_process_view: bool = false; lh.debug(&format!("GPU has fans = {}", have_fans)); @@ -47,16 +50,27 @@ pub fn run( let layout = Layout::default() .direction(Direction::Vertical) .constraints([Constraint::Min(0), Constraint::Length(1)]) - .split(f.size()); + .split(f.area()); #[cfg(target_os = "linux")] f.render_widget( - Paragraph::new("q to quit, p to rescan devices").alignment(Alignment::Right), + if show_process_view { + Paragraph::new("q to quit, p to rescan devices, ESC to return to stats").alignment(Alignment::Left) + } else { + Paragraph::new("q to quit, p to rescan devices").alignment(Alignment::Right) + }, layout[1], ); #[cfg(target_os = "windows")] - f.render_widget(Paragraph::new("q to quit"), layout[1]); + f.render_widget( + Paragraph::new(if show_process_view { + "q to quit, ESC to return to stats" + } else { + "q to quit" + }), + layout[1], + ); layout[0] } else { @@ -67,14 +81,14 @@ pub fn run( Constraint::Min(0), Constraint::Length(1), ]) - .split(f.size()); + .split(f.area()); f.render_widget( Tabs::new( gpu_list .iter() .map(|gpu| format!("[{}] {}", gpu.index, gpu.card_type)) - .collect(), + .collect::>(), ) .select(selected_gpu) .style(Style::default().fg(Color::Green)) @@ -84,7 +98,13 @@ pub fn run( ); f.render_widget( - Paragraph::new("q to quit, p to rescan devices, fn keys to switch devices"), + Paragraph::new( + if show_process_view { + "q to quit, p to rescan devices, fn keys to switch devices, ESC to return to stats" + } else { + "q to quit, p to rescan devices, fn keys to switch devices, f or / to show processes" + } + ), layout[2], ); @@ -94,7 +114,7 @@ pub fn run( // Outermost Block, which draws the green border aound the whole UI. let block = Block::default() .title("NVTOP") - .title_position(block::Position::Top) + .title_top(Line::from("NVTOP")) .title_alignment(Alignment::Center) .borders(Borders::ALL) .border_style(Style::default().fg(Color::Green)) @@ -106,7 +126,7 @@ pub fn run( .direction(Direction::Horizontal) .constraints(vec![Constraint::Percentage(70), Constraint::Percentage(30)]) .margin(1) - .split(f.size()); + .split(f.area()); { let chunks = Layout::default() @@ -126,9 +146,15 @@ pub fn run( let core_gauge = draw_core_clock(gpu).unwrap(); f.render_widget(core_gauge, chunks[1]); - // Misc: - let paragraph = draw_misc(gpu); - f.render_widget(paragraph, chunks[2]); + if !show_process_view { + // Misc (normal view): + let paragraph = draw_misc(gpu); + f.render_widget(paragraph, chunks[2]); + } else { + // Process view instead of misc + let process_widget = draw_gpu_processes(gpu); + f.render_widget(process_widget, chunks[2]); + } } { @@ -151,7 +177,15 @@ pub fn run( f.render_widget(temp_gauge, chunks[1]); // Fan speed: - if have_fans { + if have_fans && !show_process_view { + let gauge = draw_fan_speed(gpu); + f.render_widget(gauge, chunks[2]); + } else if show_process_view { + // Show additional process information or blank space + let blank_widget = draw_process_blank_space(); + f.render_widget(blank_widget, chunks[2]); + } else { + // Regular fan speed view let gauge = draw_fan_speed(gpu); f.render_widget(gauge, chunks[2]); } @@ -164,6 +198,16 @@ pub fn run( match key.code { KeyCode::Char('q') => break, + KeyCode::Esc => { + // Exit process view mode if currently in it + if show_process_view { + show_process_view = false; + } + } + KeyCode::Char('f') | KeyCode::Char('/') => { + // Toggle or enter process view mode + show_process_view = true; + } KeyCode::F(n) if (1..=gpu_list.len()).contains(&n.into()) => { selected_gpu = usize::from(n - 1) } @@ -233,7 +277,7 @@ fn draw_fan_speed<'d>(gpu: &GpuInfo<'d>) -> Gauge<'d> { .block(Block::default().borders(Borders::ALL).title("Fan Speed")) .gauge_style(calculate_severity(percentage).style_for()) .label(spanned_label) - .set_style(Style::default()) + .style(Style::default()) .ratio(percentage) } @@ -251,7 +295,7 @@ fn draw_gpu_die_temp<'d>(gpu: &GpuInfo<'d>) -> Gauge<'d> { .block(Block::default().borders(Borders::ALL).title("Temp")) .gauge_style(calculate_severity(temp_ratio).style_for()) .label(spanned_label) - .set_style(Style::default()) + .style(Style::default()) .ratio(temp_ratio) } @@ -261,6 +305,8 @@ fn draw_memory_usage<'d>(gpu: &GpuInfo<'d>) -> Gauge<'d> { free: 0, total: 0, //TODO: This never changes so put in self used: 0, + reserved: Default::default(), + version: Default::default(), }, |mem_info| mem_info, ); @@ -333,3 +379,92 @@ fn draw_core_clock<'d>(gpu: &GpuInfo<'d>) -> Result, NvTopError> { .label(spanned_label) .ratio(percentage)) } + +// Placeholder for drawing GPU processes - need to implement actual process retrieval +fn draw_gpu_processes<'d>(gpu: &GpuInfo<'d>) -> Paragraph<'d> { + let block = Block::default().borders(Borders::ALL).title(Span::styled( + "GPU Processes", + Style::default() + .fg(Color::Yellow) + .add_modifier(Modifier::BOLD), + )); + + let processes_text = match get_gpu_processes(gpu) { + Ok(processes) => { + if processes.is_empty() { + "No processes running".to_string() + } else { + let mut text = String::from("PID\t\tMemory\n"); + for proc in processes.iter().take(10) { // Show top 10 processes + text.push_str(&format!("{}\t\t{} MB\n", proc.pid, proc.used_memory / 1024 / 1024)); // Convert to MB + } + text + } + }, + Err(_) => { + // If there's an issue with the specific API, try a more general approach + match gpu.inner.running_graphics_processes() { + Ok(graphics_processes) => { + if graphics_processes.is_empty() { + "No processes running".to_string() + } else { + let mut text = String::from("PID\t\tMemory\n"); + for process in graphics_processes.iter().take(10) { + // Try to extract memory value - if not available, use 0 + let memory_mb = 0; // Placeholder until we determine correct API + text.push_str(&format!("{}\t\t{} MB\n", process.pid, memory_mb)); + } + text + } + }, + Err(_) => "Error retrieving processes".to_string(), + } + }, + }; + + Paragraph::new(processes_text) + .block(block) + .wrap(Wrap { trim: false }) + .scroll((0, 0)) +} + +fn draw_process_blank_space<'d>() -> Paragraph<'d> { + let block = Block::default().borders(Borders::ALL).title(" "); + Paragraph::new("") + .block(block) + .wrap(Wrap { trim: true }) +} + +// Structure to represent a process running on the GPU +#[derive(Debug)] +struct GpuProcess { + pid: u32, + used_memory: u64, +} + +// Function to retrieve processes running on the GPU (placeholder until API is properly identified) +fn get_gpu_processes<'d>(gpu: &GpuInfo<'d>) -> Result, NvmlError> { + // Try various possible methods to get running processes based on nvml-wrapper API + // This might need adjustment based on the specific version + + // First, try the compute processes method + match gpu.inner.running_compute_processes() { + Ok(processes) => { + let mut gpu_processes = Vec::new(); + for process in processes { + // Since the exact API varies by version, use a default value + let memory_value = 0; // Placeholder until we determine correct API access + + gpu_processes.push(GpuProcess { + pid: process.pid, + used_memory: memory_value, + }); + } + Ok(gpu_processes) + }, + Err(_) => { + // If that doesn't work, return empty vector + Ok(Vec::new()) + } + } +} diff --git a/src/gpu.rs b/src/gpu.rs index 42929cf..ceddc40 100644 --- a/src/gpu.rs +++ b/src/gpu.rs @@ -87,7 +87,7 @@ impl fmt::Display for GpuInfo<'_> { [Clock::Graphics, Clock::SM, Clock::Memory, Clock::Video] .into_iter() .for_each(|clock_type| { - match self.inner.clock(clock_type.clone(), clock_id.clone()) { + match self.inner.clock(clock_type, clock_id) { Ok(value) => { writeln!(f, "Clock {:?} for {:?}: {}", clock_type, clock_id, value) .unwrap_or_default() From 0830dbf33d7fe8c1ff4aedeb02193cadc55ba244 Mon Sep 17 00:00:00 2001 From: jer Date: Tue, 20 Jan 2026 15:19:55 +1100 Subject: [PATCH 02/10] chore: functional(ish) search on processes... --- Cargo.lock | 645 +++++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 1 + src/app.rs | 385 ++++++++++++++++++++++++-------- src/gpu.rs | 22 +- 4 files changed, 930 insertions(+), 123 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c6432c..87e77bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.21" @@ -73,6 +82,12 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "atomic" version = "0.6.1" @@ -82,6 +97,17 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -94,6 +120,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + [[package]] name = "bit-set" version = "0.5.3" @@ -151,6 +183,16 @@ dependencies = [ "rustversion", ] +[[package]] +name = "cc" +version = "1.2.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" +dependencies = [ + "find-msvc-tools", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -163,6 +205,35 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chrono" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_lex 0.2.4", + "indexmap", + "once_cell", + "strsim 0.10.0", + "termcolor", + "textwrap", +] + [[package]] name = "clap" version = "4.5.54" @@ -181,8 +252,8 @@ checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", - "clap_lex", - "strsim", + "clap_lex 0.7.7", + "strsim 0.11.1", ] [[package]] @@ -197,6 +268,15 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "clap_lex" version = "0.7.7" @@ -232,6 +312,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -241,6 +327,62 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crossterm" version = "0.29.0" @@ -288,6 +430,16 @@ dependencies = [ "phf", ] +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + [[package]] name = "darling" version = "0.20.11" @@ -308,6 +460,20 @@ dependencies = [ "darling_macro 0.23.0", ] +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + [[package]] name = "darling_core" version = "0.20.11" @@ -318,7 +484,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.11.1", "syn 2.0.114", ] @@ -331,10 +497,21 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.11.1", "syn 2.0.114", ] +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core 0.14.4", + "quote", + "syn 1.0.109", +] + [[package]] name = "darling_macro" version = "0.20.11" @@ -357,6 +534,16 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "defer-drop" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f613ec9fa66a6b28cdb1842b27f9adf24f39f9afc4dcdd9fdecee4aca7945c57" +dependencies = [ + "crossbeam-channel", + "once_cell", +] + [[package]] name = "deltae" version = "0.3.2" @@ -372,6 +559,37 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive_builder" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" +dependencies = [ + "darling 0.14.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" +dependencies = [ + "derive_builder_core", + "syn 1.0.109", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -404,6 +622,27 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "document-features" version = "0.2.12" @@ -419,6 +658,19 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -465,6 +717,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" + [[package]] name = "finl_unicode" version = "1.4.0" @@ -489,6 +747,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "fuzzy-matcher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" +dependencies = [ + "thread_local", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -499,6 +766,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -511,6 +789,12 @@ dependencies = [ "wasip2", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.16.1" @@ -528,18 +812,67 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "ident_case" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "indoc" version = "2.0.7" @@ -599,7 +932,7 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fe90c1150662e858c7d5f945089b7517b0a80d8bf7ba4b1b5ffc984e7230a5b" dependencies = [ - "hashbrown", + "hashbrown 0.16.1", "portable-atomic", "thiserror 2.0.18", ] @@ -632,6 +965,16 @@ dependencies = [ "windows-link", ] +[[package]] +name = "libredox" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +dependencies = [ + "bitflags 2.10.0", + "libc", +] + [[package]] name = "line-clipping" version = "0.3.5" @@ -674,7 +1017,7 @@ version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" dependencies = [ - "hashbrown", + "hashbrown 0.16.1", ] [[package]] @@ -683,7 +1026,7 @@ version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303" dependencies = [ - "nix", + "nix 0.29.0", "winapi", ] @@ -699,6 +1042,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.9.1" @@ -726,6 +1078,31 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.6.5", + "pin-utils", +] + [[package]] name = "nix" version = "0.29.0" @@ -736,7 +1113,7 @@ dependencies = [ "cfg-if", "cfg_aliases", "libc", - "memoffset", + "memoffset 0.9.1", ] [[package]] @@ -812,10 +1189,11 @@ name = "nvtop" version = "0.1.2" dependencies = [ "anyhow", - "clap", + "clap 4.5.54", "crossterm", "nvml-wrapper", "ratatui", + "skim", "thiserror 2.0.18", ] @@ -840,6 +1218,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + [[package]] name = "parking_lot" version = "0.12.5" @@ -958,6 +1342,12 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "portable-atomic" version = "1.13.0" @@ -1031,7 +1421,7 @@ checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" dependencies = [ "bitflags 2.10.0", "compact_str", - "hashbrown", + "hashbrown 0.16.1", "indoc", "itertools", "kasuari", @@ -1040,7 +1430,7 @@ dependencies = [ "thiserror 2.0.18", "unicode-segmentation", "unicode-truncate", - "unicode-width", + "unicode-width 0.2.2", ] [[package]] @@ -1082,7 +1472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" dependencies = [ "bitflags 2.10.0", - "hashbrown", + "hashbrown 0.16.1", "indoc", "instability", "itertools", @@ -1091,7 +1481,27 @@ dependencies = [ "strum", "time", "unicode-segmentation", - "unicode-width", + "unicode-width 0.2.2", +] + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", ] [[package]] @@ -1103,6 +1513,17 @@ dependencies = [ "bitflags 2.10.0", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 1.0.69", +] + [[package]] name = "regex" version = "1.12.2" @@ -1219,6 +1640,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook" version = "0.3.18" @@ -1256,6 +1683,35 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +[[package]] +name = "skim" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d28de0a6cb2cdd83a076f1de9d965b973ae08b244df1aa70b432946dda0f32" +dependencies = [ + "atty", + "beef", + "bitflags 1.3.2", + "chrono", + "clap 3.2.25", + "crossbeam", + "defer-drop", + "derive_builder", + "env_logger", + "fuzzy-matcher", + "lazy_static", + "log", + "nix 0.25.1", + "rayon", + "regex", + "shlex", + "time", + "timer", + "tuikit", + "unicode-width 0.1.14", + "vte", +] + [[package]] name = "smallvec" version = "1.15.1" @@ -1268,6 +1724,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strsim" version = "0.11.1" @@ -1317,6 +1779,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "term" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" +dependencies = [ + "dirs-next", + "rustversion", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "terminfo" version = "0.9.0" @@ -1356,7 +1838,7 @@ dependencies = [ "libc", "log", "memmem", - "nix", + "nix 0.29.0", "num-derive", "num-traits", "ordered-float", @@ -1380,6 +1862,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" + [[package]] name = "thiserror" version = "1.0.69" @@ -1420,6 +1908,15 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + [[package]] name = "time" version = "0.3.45" @@ -1441,6 +1938,29 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" +[[package]] +name = "timer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d42176308937165701f50638db1c31586f183f1aab416268216577aec7306b" +dependencies = [ + "chrono", +] + +[[package]] +name = "tuikit" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e19c6ab038babee3d50c8c12ff8b910bdb2196f62278776422f50390d8e53d8" +dependencies = [ + "bitflags 1.3.2", + "lazy_static", + "log", + "nix 0.24.3", + "term", + "unicode-width 0.1.14", +] + [[package]] name = "typenum" version = "1.19.0" @@ -1473,9 +1993,15 @@ checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5" dependencies = [ "itertools", "unicode-segmentation", - "unicode-width", + "unicode-width 0.2.2", ] +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "unicode-width" version = "0.2.2" @@ -1495,7 +2021,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "atomic", - "getrandom", + "getrandom 0.3.4", "js-sys", "wasm-bindgen", ] @@ -1506,6 +2032,27 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vte" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" +dependencies = [ + "arrayvec", + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "vtparse" version = "0.6.2" @@ -1591,7 +2138,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692daff6d93d94e29e4114544ef6d5c942a7ed998b37abdc19b17136ea428eb7" dependencies = [ - "getrandom", + "getrandom 0.3.4", "mac_address", "sha2", "thiserror 1.0.69", @@ -1618,7 +2165,7 @@ checksum = "5f2ab60e120fd6eaa68d9567f3226e876684639d22a4219b313ff69ec0ccd5ac" dependencies = [ "log", "ordered-float", - "strsim", + "strsim 0.11.1", "thiserror 1.0.69", "wezterm-dynamic-derive", ] @@ -1663,18 +2210,80 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.114", +] + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.61.2" diff --git a/Cargo.toml b/Cargo.toml index b9c1623..00f6d4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,5 @@ clap = { version = "4.5.54", features = ["derive"] } crossterm = "0.29.0" nvml-wrapper = "0.11.0" ratatui = "0.30.0" +skim = "0.10.4" thiserror = "2.0.18" diff --git a/src/app.rs b/src/app.rs index e20f14e..0688f44 100644 --- a/src/app.rs +++ b/src/app.rs @@ -7,8 +7,8 @@ use ratatui::symbols::DOT; use ratatui::{prelude::*, widgets::*}; use ratatui::{ prelude::{CrosstermBackend, Terminal}, - widgets::Paragraph, widgets::Block, + widgets::Paragraph, }; use std::time::Duration; @@ -35,9 +35,11 @@ pub fn run( let mut have_fans: bool = gpu_list .iter() .any(|gpu| gpu.inner.num_fans().map_or(0, |fc| fc) != 0); - + // State variables for process view - let mut show_process_view: bool = false; + let mut show_process_view: bool = true; // Always show process view as requested + let mut fuzzy_search_active: bool = false; + let mut fuzzy_search_input: String = String::new(); lh.debug(&format!("GPU has fans = {}", have_fans)); @@ -67,7 +69,7 @@ pub fn run( Paragraph::new(if show_process_view { "q to quit, ESC to return to stats" } else { - "q to quit" + "q to quit" }), layout[1], ); @@ -97,14 +99,13 @@ pub fn run( layout[0], ); + // Render the main footer text f.render_widget( - Paragraph::new( - if show_process_view { - "q to quit, p to rescan devices, fn keys to switch devices, ESC to return to stats" - } else { - "q to quit, p to rescan devices, fn keys to switch devices, f or / to show processes" - } - ), + if show_process_view { + Paragraph::new("q to quit, p to rescan devices, fn keys to switch devices, ESC to return to stats, f or / to search".to_string()) + } else { + Paragraph::new("q to quit, p to rescan devices, fn keys to switch devices, f or / to show processes".to_string()) + }, layout[2], ); @@ -151,8 +152,8 @@ pub fn run( let paragraph = draw_misc(gpu); f.render_widget(paragraph, chunks[2]); } else { - // Process view instead of misc - let process_widget = draw_gpu_processes(gpu); + // Combined misc and processes view + let process_widget = draw_misc_with_processes(gpu, &fuzzy_search_input); f.render_widget(process_widget, chunks[2]); } } @@ -177,19 +178,40 @@ pub fn run( f.render_widget(temp_gauge, chunks[1]); // Fan speed: - if have_fans && !show_process_view { - let gauge = draw_fan_speed(gpu); - f.render_widget(gauge, chunks[2]); - } else if show_process_view { - // Show additional process information or blank space - let blank_widget = draw_process_blank_space(); - f.render_widget(blank_widget, chunks[2]); - } else { - // Regular fan speed view + if have_fans { let gauge = draw_fan_speed(gpu); f.render_widget(gauge, chunks[2]); } } + + // If fuzzy search is active, render a modal-style search box in the center as an overlay + if fuzzy_search_active { + let search_text = format!("{}_", &fuzzy_search_input); + let block = Block::default() + .borders(Borders::ALL) + .title("🔍 Fuzzy Search - Type to search (ESC to cancel)") + .border_style(Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)); + + let search_input = Paragraph::new(search_text) + .block(block) + .style(Style::default().bg(Color::Rgb(40, 40, 40)).fg(Color::LightCyan).add_modifier(Modifier::BOLD)) + .alignment(Alignment::Left); + + // Calculate centered position for the search box + let search_width = 60.min(f.area().width.saturating_sub(2)); + let search_height = 3; + let x = f.area().width / 2 - search_width / 2; + let y = f.area().height / 2 - 1; // Center vertically + + let search_area = Rect::new(x, y, search_width, search_height); + + // Draw a semi-transparent overlay background + let overlay = Paragraph::new(" ") + .style(Style::default().bg(Color::Rgb(20, 20, 20))); // Very dark semi-transparent + + f.render_widget(overlay, f.area()); + f.render_widget(search_input, search_area); + } })?; if crossterm::event::poll(std::time::Duration::from_millis(250))? { @@ -199,16 +221,59 @@ pub fn run( match key.code { KeyCode::Char('q') => break, KeyCode::Esc => { - // Exit process view mode if currently in it - if show_process_view { + // Exit fuzzy search if active, otherwise exit process view mode if currently in it + if fuzzy_search_active { + fuzzy_search_active = false; + fuzzy_search_input.clear(); + } else if show_process_view { show_process_view = false; } } - KeyCode::Char('f') | KeyCode::Char('/') => { - // Toggle or enter process view mode + KeyCode::Char('f') => { + // Always activate fuzzy search when 'f' is pressed + show_process_view = true; + fuzzy_search_active = true; + fuzzy_search_input.clear(); + } + KeyCode::Char('/') => { + // Always activate fuzzy search when '/' is pressed show_process_view = true; + fuzzy_search_active = true; + fuzzy_search_input.clear(); + } + // Handle F keys for fuzzy search + KeyCode::F(1) + | KeyCode::F(2) + | KeyCode::F(3) + | KeyCode::F(4) + | KeyCode::F(5) + | KeyCode::F(6) + | KeyCode::F(7) + | KeyCode::F(8) + | KeyCode::F(9) + | KeyCode::F(10) + | KeyCode::F(11) + | KeyCode::F(12) => { + if !fuzzy_search_active { + fuzzy_search_active = true; + fuzzy_search_input.clear(); + } + } + // Handle character input for fuzzy search - but exclude 'f' and '/' which are handled separately + KeyCode::Char(c) if fuzzy_search_active => { + fuzzy_search_input.push(c); } - KeyCode::F(n) if (1..=gpu_list.len()).contains(&n.into()) => { + // Handle backspace in fuzzy search + KeyCode::Backspace if fuzzy_search_active => { + fuzzy_search_input.pop(); + } + // Handle Enter to exit fuzzy search + KeyCode::Enter if fuzzy_search_active => { + fuzzy_search_active = false; + } + KeyCode::F(n) + if (1..=gpu_list.len()).contains(&n.into()) && !fuzzy_search_active => + { selected_gpu = usize::from(n - 1) } @@ -340,6 +405,77 @@ fn draw_misc<'d>(gpu: &'d GpuInfo<'d>) -> Paragraph<'d> { .wrap(Wrap { trim: true }) } +fn draw_misc_with_processes<'d>(gpu: &GpuInfo<'d>, search_term: &str) -> Paragraph<'d> { + let block = Block::default().borders(Borders::ALL).title(Span::styled( + "Misc/Processes", + Style::default() + .fg(Color::Magenta) + .add_modifier(Modifier::BOLD), + )); + + let content = match get_gpu_processes(gpu) { + Ok(mut processes) => { + if processes.is_empty() { + format!("{}\n\nNo processes running", gpu.misc) + } else { + // Filter and rank processes based on search term if provided + if !search_term.is_empty() { + // Create pairs of (process, score) and filter by positive scores + let mut scored_processes: Vec<(GpuProcess, i32)> = Vec::new(); + + for proc in processes { + let name_score = fuzzy_score(&proc.name, search_term); + let pid_score = fuzzy_score(&proc.pid.to_string(), search_term); + + // Use the higher of the two scores + let max_score = name_score.max(pid_score); + + if max_score > 0 { + scored_processes.push((proc, max_score)); + } + } + + // Sort by score (descending) - highest scores first + scored_processes.sort_by(|a, b| b.1.cmp(&a.1)); + + // Extract just the processes in ranked order + processes = scored_processes.into_iter().map(|(proc, _)| proc).collect(); + } else { + // Sort by memory usage when not searching + processes.sort_by(|a, b| b.used_memory.cmp(&a.used_memory)); + } + + if processes.is_empty() && !search_term.is_empty() { + format!("{}\n\nNo processes match '{}'", gpu.misc, search_term) + } else { + let mut content = gpu.misc.clone(); + content.push_str("\n\nProcesses:\n"); + content.push_str("Name PID Memory(MB)\n"); + for proc in processes.iter().take(10) { + // Show top 10 processes + let memory_mb = proc.used_memory / 1024 / 1024; // Convert bytes to MB + // Format with right-aligned memory usage + content.push_str(&format!( + "{:<20} {:>10} {:>10} MB\n", + proc.name, proc.pid, memory_mb + )); + } + content + } + } + } + Err(_) => { + // Fallback: show misc info with error message + format!("{}\n\nError retrieving processes", gpu.misc) + } + }; + + Paragraph::new(content) + .block(block) + .wrap(Wrap { trim: false }) + .scroll((0, 0)) +} + fn draw_core_utilisation<'d>(gpu: &GpuInfo<'d>) -> Gauge<'d> { let utilisation_rates = gpu.inner.utilization_rates(); let percent = utilisation_rates.map_or(0, |ur| ur.gpu as u16); @@ -380,91 +516,154 @@ fn draw_core_clock<'d>(gpu: &GpuInfo<'d>) -> Result, NvTopError> { .ratio(percentage)) } -// Placeholder for drawing GPU processes - need to implement actual process retrieval -fn draw_gpu_processes<'d>(gpu: &GpuInfo<'d>) -> Paragraph<'d> { - let block = Block::default().borders(Borders::ALL).title(Span::styled( - "GPU Processes", - Style::default() - .fg(Color::Yellow) - .add_modifier(Modifier::BOLD), - )); +// Helper function for fuzzy matching using a more sophisticated algorithm +fn fuzzy_match(text: &str, pattern: &str) -> bool { + if pattern.is_empty() { + return true; + } - let processes_text = match get_gpu_processes(gpu) { - Ok(processes) => { - if processes.is_empty() { - "No processes running".to_string() + let text_chars: Vec = text.chars().collect(); + let pattern_chars: Vec = pattern.chars().collect(); + + let mut text_idx = 0; + let mut pattern_idx = 0; + + while text_idx < text_chars.len() && pattern_idx < pattern_chars.len() { + if text_chars[text_idx] + .to_lowercase() + .eq(pattern_chars[pattern_idx].to_lowercase()) + { + pattern_idx += 1; + } + text_idx += 1; + } + + pattern_idx == pattern_chars.len() +} + +// Helper function to score fuzzy matches (higher score = better match) +fn fuzzy_score(text: &str, pattern: &str) -> i32 { + if pattern.is_empty() { + return 0; + } + + let text_lower = text.to_lowercase(); + let pattern_lower = pattern.to_lowercase(); + + if !fuzzy_match(text, pattern) { + return -1; // No match + } + + // Calculate score based on match quality + let mut score = 0; + let text_chars: Vec = text_lower.chars().collect(); + let pattern_chars: Vec = pattern_lower.chars().collect(); + + let mut text_idx = 0; + let mut pattern_idx = 0; + let mut consecutive_bonus = 0; + + // Simple scoring algorithm: find matches and give bonuses for consecutive letters + while text_idx < text_chars.len() && pattern_idx < pattern_chars.len() { + if text_chars[text_idx] == pattern_chars[pattern_idx] { + // Bonus for consecutive matches + if text_idx > 0 && pattern_idx > 0 && + text_chars[text_idx - 1] == pattern_chars[pattern_idx - 1] { + consecutive_bonus += 10; } else { - let mut text = String::from("PID\t\tMemory\n"); - for proc in processes.iter().take(10) { // Show top 10 processes - text.push_str(&format!("{}\t\t{} MB\n", proc.pid, proc.used_memory / 1024 / 1024)); // Convert to MB - } - text + consecutive_bonus = 10; // Base bonus for a match } - }, - Err(_) => { - // If there's an issue with the specific API, try a more general approach - match gpu.inner.running_graphics_processes() { - Ok(graphics_processes) => { - if graphics_processes.is_empty() { - "No processes running".to_string() - } else { - let mut text = String::from("PID\t\tMemory\n"); - for process in graphics_processes.iter().take(10) { - // Try to extract memory value - if not available, use 0 - let memory_mb = 0; // Placeholder until we determine correct API - text.push_str(&format!("{}\t\t{} MB\n", process.pid, memory_mb)); - } - text - } - }, - Err(_) => "Error retrieving processes".to_string(), - } - }, - }; - Paragraph::new(processes_text) - .block(block) - .wrap(Wrap { trim: false }) - .scroll((0, 0)) -} + score += consecutive_bonus + 5; // Base score plus consecutive bonus + pattern_idx += 1; + } else { + consecutive_bonus = 0; // Reset consecutive bonus + } -fn draw_process_blank_space<'d>() -> Paragraph<'d> { - let block = Block::default().borders(Borders::ALL).title(" "); - Paragraph::new("") - .block(block) - .wrap(Wrap { trim: true }) + text_idx += 1; + } + + score } -// Structure to represent a process running on the GPU +// Structure to represent a process running on the GPU #[derive(Debug)] struct GpuProcess { pid: u32, used_memory: u64, + name: String, // Process name } -// Function to retrieve processes running on the GPU (placeholder until API is properly identified) +// Function to retrieve processes running on the GPU fn get_gpu_processes<'d>(gpu: &GpuInfo<'d>) -> Result, NvmlError> { - // Try various possible methods to get running processes based on nvml-wrapper API - // This might need adjustment based on the specific version - - // First, try the compute processes method - match gpu.inner.running_compute_processes() { - Ok(processes) => { - let mut gpu_processes = Vec::new(); - for process in processes { - // Since the exact API varies by version, use a default value - let memory_value = 0; // Placeholder until we determine correct API access - + let mut gpu_processes = Vec::new(); + + // Try to get compute processes + if let Ok(compute_processes) = gpu.inner.running_compute_processes() { + for process in compute_processes { + let name = match std::fs::read_to_string(format!("/proc/{}/comm", process.pid)) { + Ok(name) => name.trim().to_string(), + Err(_) => format!("Process-{}", process.pid), // Fallback if we can't get the name + }; + + // Access memory value for compute processes - it's a direct UsedGpuMemory value + let memory_value = extract_memory_value(process.used_gpu_memory); + + gpu_processes.push(GpuProcess { + pid: process.pid, + used_memory: memory_value, + name, + }); + } + } + + // Also try to get graphics processes to get a complete picture + if let Ok(graphics_processes) = gpu.inner.running_graphics_processes() { + for process in graphics_processes { + // Check if this process is already in our list (from compute processes) + if !gpu_processes.iter().any(|p| p.pid == process.pid) { + let name = match std::fs::read_to_string(format!("/proc/{}/comm", process.pid)) { + Ok(name) => name.trim().to_string(), + Err(_) => format!("Process-{}", process.pid), // Fallback if we can't get the name + }; + + // Extract memory value for graphics process - based on the error, it's UsedGpuMemory (not optional) + let memory_value = extract_memory_value(process.used_gpu_memory); + gpu_processes.push(GpuProcess { pid: process.pid, used_memory: memory_value, + name, }); } - Ok(gpu_processes) - }, - Err(_) => { - // If that doesn't work, return empty vector - Ok(Vec::new()) } } + + Ok(gpu_processes) +} + +// Helper function to extract the memory value from UsedGpuMemory enum +// Using a direct approach with the actual enum structure +fn extract_memory_value(used_memory: nvml_wrapper::enums::device::UsedGpuMemory) -> u64 { + // Since we can't determine the exact API, let's use a different approach + // by converting the enum to a string and extracting the value + let s = format!("{:?}", used_memory); + + // Look for the pattern where the memory value appears in the debug output + // For example, if it looks like "Unrestricted(123456)" or similar + if let Some(start) = s.find('(') { + if let Some(end) = s.find(')') { + if start < end { + let num_str = &s[start + 1..end]; + return num_str.parse::().unwrap_or(0); + } + } + } + + // If the pattern isn't found, try to extract any number from the string + s.chars() + .filter(|c| c.is_ascii_digit()) + .collect::() + .parse::() + .unwrap_or(0) } diff --git a/src/gpu.rs b/src/gpu.rs index ceddc40..aaa13a6 100644 --- a/src/gpu.rs +++ b/src/gpu.rs @@ -86,18 +86,16 @@ impl fmt::Display for GpuInfo<'_> { .for_each(|clock_id| { [Clock::Graphics, Clock::SM, Clock::Memory, Clock::Video] .into_iter() - .for_each(|clock_type| { - match self.inner.clock(clock_type, clock_id) { - Ok(value) => { - writeln!(f, "Clock {:?} for {:?}: {}", clock_type, clock_id, value) - .unwrap_or_default() - } - Err(err) => { - let _formatted = format!( - "clock_type={:?}\t\tclock_id={:?} {}", - clock_type, clock_id, err, - ); - } + .for_each(|clock_type| match self.inner.clock(clock_type, clock_id) { + Ok(value) => { + writeln!(f, "Clock {:?} for {:?}: {}", clock_type, clock_id, value) + .unwrap_or_default() + } + Err(err) => { + let _formatted = format!( + "clock_type={:?}\t\tclock_id={:?} {}", + clock_type, clock_id, err, + ); } }); }); From 973db8cc86531cf05e5382df68fb822ade117679 Mon Sep 17 00:00:00 2001 From: jer Date: Tue, 20 Jan 2026 16:01:23 +1100 Subject: [PATCH 03/10] feat: functional fuzzy-find and porcesses.. --- src/app.rs | 357 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 285 insertions(+), 72 deletions(-) diff --git a/src/app.rs b/src/app.rs index 0688f44..b4aa57d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -18,6 +18,13 @@ use crate::stylers::calculate_severity; use crate::termite::LoggingHandle; use crate::{errors, gpu::GpuInfo}; +#[derive(Clone, PartialEq)] +enum ProcessSortBy { + Memory, + Name, + Pid, +} + pub fn run( nvml: nvml_wrapper::Nvml, delay: Duration, @@ -41,6 +48,14 @@ pub fn run( let mut fuzzy_search_active: bool = false; let mut fuzzy_search_input: String = String::new(); + let mut sort_by: ProcessSortBy = ProcessSortBy::Memory; + let mut sort_reverse: bool = true; // Descending by default + + // State for process selection + let mut selected_process_pid: Option = None; + let mut process_selection_enabled: bool = false; + let mut highlighted_process_index: usize = 0; + lh.debug(&format!("GPU has fans = {}", have_fans)); loop { @@ -102,7 +117,11 @@ pub fn run( // Render the main footer text f.render_widget( if show_process_view { - Paragraph::new("q to quit, p to rescan devices, fn keys to switch devices, ESC to return to stats, f or / to search".to_string()) + if process_selection_enabled { + Paragraph::new("q to quit, ESC to return to stats, f or / to search, s to sort, r to reverse, UP/DOWN to navigate, SPACE to select, p to exit".to_string()) + } else { + Paragraph::new("q to quit, p to rescan devices, fn keys to switch devices, ESC to return to stats, f or / to search, s to sort, r to reverse, p to select process".to_string()) + } } else { Paragraph::new("q to quit, p to rescan devices, fn keys to switch devices, f or / to show processes".to_string()) }, @@ -123,66 +142,73 @@ pub fn run( .style(Style::default()); f.render_widget(block, mid_area); - let chunks = Layout::default() - .direction(Direction::Horizontal) - .constraints(vec![Constraint::Percentage(70), Constraint::Percentage(30)]) + // Main vertical layout: top for card info, middle for metrics, bottom for processes + let main_layout = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Length(3), // Top 3 lines for card info + Constraint::Percentage(50), // Middle 50% for metrics + Constraint::Percentage(45), // Bottom 45% for processes + ]) .margin(1) .split(f.area()); - { - let chunks = Layout::default() - .constraints(vec![ - Constraint::Percentage(60), - Constraint::Percentage(20), - Constraint::Percentage(20), - ]) - .margin(1) - .split(chunks[0]); - - // Core: - let core_gauge = draw_core_utilisation(gpu); - f.render_widget(core_gauge, chunks[0]); + // Top section: Card info (single line) + let top_chunk = main_layout[0]; + let card_info_widget = draw_driver_info(gpu); + f.render_widget(card_info_widget, top_chunk); - // Core Clock: - let core_gauge = draw_core_clock(gpu).unwrap(); - f.render_widget(core_gauge, chunks[1]); - - if !show_process_view { - // Misc (normal view): - let paragraph = draw_misc(gpu); - f.render_widget(paragraph, chunks[2]); - } else { - // Combined misc and processes view - let process_widget = draw_misc_with_processes(gpu, &fuzzy_search_input); - f.render_widget(process_widget, chunks[2]); - } + // Middle section: Metrics (core util, temp, memory, etc.) + let middle_area = main_layout[1]; + let middle_chunks = Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Percentage(70), + Constraint::Percentage(30), + ]) + .split(middle_area); + + let left_middle = Layout::default() + .constraints([ + Constraint::Percentage(50), // Core utilization + Constraint::Percentage(50), // Core Clock + ]) + .split(middle_chunks[0]); + + // Core utilization + let core_gauge = draw_core_utilisation(gpu); + f.render_widget(core_gauge, left_middle[0]); + + // Core Clock + let core_gauge = draw_core_clock(gpu).unwrap(); + f.render_widget(core_gauge, left_middle[1]); + + // Right side of middle: Memory, Temp, Fan - adjusted to align with left panels + let right_middle = Layout::default() + .constraints([ + Constraint::Percentage(50), // Memory + Constraint::Percentage(25), // Temp (half height) + Constraint::Percentage(25), // Fan speed (half height) + ]) + .split(middle_chunks[1]); + + // Memory: + let mem_usage_gauge = draw_memory_usage(gpu); + f.render_widget(mem_usage_gauge, right_middle[0]); + + // Temp: + let temp_gauge = draw_gpu_die_temp(gpu); + f.render_widget(temp_gauge, right_middle[1]); + + // Fan speed: + if have_fans { + let gauge = draw_fan_speed(gpu); + f.render_widget(gauge, right_middle[2]); } - { - let chunks = Layout::default() - .constraints([ - Constraint::Percentage(33), - Constraint::Percentage(33), - Constraint::Percentage(33), - ]) - .direction(Direction::Vertical) - .margin(1) - .split(chunks[1]); - - // Memory: - let mem_usage_gauge = draw_memory_usage(gpu); - f.render_widget(mem_usage_gauge, chunks[0]); - - // Temp: - let temp_gauge = draw_gpu_die_temp(gpu); - f.render_widget(temp_gauge, chunks[1]); - - // Fan speed: - if have_fans { - let gauge = draw_fan_speed(gpu); - f.render_widget(gauge, chunks[2]); - } - } + // Bottom section: Processes (always shown) + let process_widget = draw_misc_with_processes(gpu, &fuzzy_search_input, &sort_by, sort_reverse, process_selection_enabled, highlighted_process_index, selected_process_pid); + f.render_widget(process_widget, main_layout[2]); // If fuzzy search is active, render a modal-style search box in the center as an overlay if fuzzy_search_active { @@ -271,6 +297,50 @@ pub fn run( KeyCode::Enter if fuzzy_search_active => { fuzzy_search_active = false; } + // Sorting controls + KeyCode::Char('s') => { + // Cycle through sorting options: Memory -> Name -> PID -> Memory... + sort_by = match sort_by { + ProcessSortBy::Memory => ProcessSortBy::Name, + ProcessSortBy::Name => ProcessSortBy::Pid, + ProcessSortBy::Pid => ProcessSortBy::Memory, + }; + } + KeyCode::Char('r') => { + // Reverse sort order + sort_reverse = !sort_reverse; + } + // Process selection controls + KeyCode::Char('p') if show_process_view && !fuzzy_search_active => { + // Toggle process selection mode + process_selection_enabled = !process_selection_enabled; + if !process_selection_enabled { + selected_process_pid = None; + } + } + KeyCode::Char(' ') if process_selection_enabled && show_process_view => { + // Select the currently highlighted process + if let Ok(processes) = get_gpu_processes(&gpu_list[selected_gpu]) { + if highlighted_process_index < processes.len() { + if let Some(proc) = processes.get(highlighted_process_index) { + selected_process_pid = Some(proc.pid); + process_selection_enabled = false; + } + } + } + } + KeyCode::Up if process_selection_enabled && show_process_view => { + // Move highlight up + highlighted_process_index = highlighted_process_index.saturating_sub(1); + } + KeyCode::Down if process_selection_enabled && show_process_view => { + // Move highlight down + if let Ok(processes) = get_gpu_processes(&gpu_list[selected_gpu]) { + if highlighted_process_index < processes.len().saturating_sub(1) { + highlighted_process_index += 1; + } + } + } KeyCode::F(n) if (1..=gpu_list.len()).contains(&n.into()) && !fuzzy_search_active => { @@ -405,9 +475,93 @@ fn draw_misc<'d>(gpu: &'d GpuInfo<'d>) -> Paragraph<'d> { .wrap(Wrap { trim: true }) } -fn draw_misc_with_processes<'d>(gpu: &GpuInfo<'d>, search_term: &str) -> Paragraph<'d> { +// Function to draw the driver info panel +fn draw_driver_info<'d>(gpu: &GpuInfo<'d>) -> Paragraph<'d> { let block = Block::default().borders(Borders::ALL).title(Span::styled( - "Misc/Processes", + "Driver Info", + Style::default() + .fg(Color::Blue) + .add_modifier(Modifier::BOLD), + )); + + let compute_cap = match gpu.inner.cuda_compute_capability() { + Ok(cap) => format!("{}.{}", cap.major, cap.minor), + Err(_) => "N/A".to_string(), + }; + + let info_text = format!( + "Card: {}\nDriver: {}\nCUDA: {:.1}\nCompute Cap: {}", + gpu.card_type, + gpu.driver_version, + gpu.cuda_version / 1000.0, + compute_cap + ); + + Paragraph::new(info_text) + .block(block) + .wrap(Wrap { trim: true }) +} + +// Helper function to get process-specific information +fn get_process_specific_info<'d>(gpu: &GpuInfo<'d>, pid: u32) -> String { + match get_gpu_processes(gpu) { + Ok(processes) => { + if let Some(proc) = processes.iter().find(|p| p.pid == pid) { + format!( + "PID: {}\nProcess: {}\nMemory: {} MB", + proc.pid, + proc.name, + proc.used_memory / 1024 / 1024 + ) + } else { + format!("PID: {}\nProcess: Unknown\nMemory: N/A", pid) + } + } + Err(_) => { + format!("PID: {}\nProcess: Error retrieving info", pid) + } + } +} + +// Function to draw process-specific info +fn draw_process_info<'d>(_gpu: &GpuInfo<'d>, process_info: &'d str) -> Paragraph<'d> { + let block = Block::default().borders(Borders::ALL).title(Span::styled( + "Selected Process", + Style::default() + .fg(Color::Cyan) + .add_modifier(Modifier::BOLD), + )); + + Paragraph::new(process_info) + .block(block) + .wrap(Wrap { trim: true }) +} + +// Function to draw process-specific clock info +fn draw_process_clock_info<'d>(_gpu: &GpuInfo<'d>, process_info: &'d str) -> Paragraph<'d> { + let block = Block::default().borders(Borders::ALL).title(Span::styled( + "Process Details", + Style::default() + .fg(Color::Cyan) + .add_modifier(Modifier::BOLD), + )); + + Paragraph::new(process_info) + .block(block) + .wrap(Wrap { trim: true }) +} + +fn draw_misc_with_processes<'d>( + gpu: &GpuInfo<'d>, + search_term: &str, + sort_by: &ProcessSortBy, + sort_reverse: bool, + process_selection_enabled: bool, + highlighted_process_index: usize, + selected_process_pid: Option, +) -> Paragraph<'d> { + let block = Block::default().borders(Borders::ALL).title(Span::styled( + "GPU Processes", Style::default() .fg(Color::Magenta) .add_modifier(Modifier::BOLD), @@ -441,24 +595,81 @@ fn draw_misc_with_processes<'d>(gpu: &GpuInfo<'d>, search_term: &str) -> Paragra // Extract just the processes in ranked order processes = scored_processes.into_iter().map(|(proc, _)| proc).collect(); } else { - // Sort by memory usage when not searching - processes.sort_by(|a, b| b.used_memory.cmp(&a.used_memory)); + // Sort by selected column when not searching + match sort_by { + ProcessSortBy::Memory => { + if sort_reverse { + processes.sort_by(|a, b| b.used_memory.cmp(&a.used_memory)); + // Descending + } else { + processes.sort_by(|a, b| a.used_memory.cmp(&b.used_memory)); + // Ascending + } + } + ProcessSortBy::Name => { + if sort_reverse { + processes.sort_by(|a, b| { + b.name.to_lowercase().cmp(&a.name.to_lowercase()) + }); // Descending + } else { + processes.sort_by(|a, b| { + a.name.to_lowercase().cmp(&b.name.to_lowercase()) + }); // Ascending + } + } + ProcessSortBy::Pid => { + if sort_reverse { + processes.sort_by(|a, b| b.pid.cmp(&a.pid)); // Descending + } else { + processes.sort_by(|a, b| a.pid.cmp(&b.pid)); // Ascending + } + } + } } if processes.is_empty() && !search_term.is_empty() { - format!("{}\n\nNo processes match '{}'", gpu.misc, search_term) + format!("No processes match '{}'", search_term) } else { - let mut content = gpu.misc.clone(); - content.push_str("\n\nProcesses:\n"); - content.push_str("Name PID Memory(MB)\n"); - for proc in processes.iter().take(10) { - // Show top 10 processes + // Show only processes without redundant header + let mut content = String::new(); + content.push_str("Name PID Memory(MB) Type\n"); + for (idx, proc) in processes.iter().take(20).enumerate() { + // Show top 20 processes let memory_mb = proc.used_memory / 1024 / 1024; // Convert bytes to MB - // Format with right-aligned memory usage - content.push_str(&format!( - "{:<20} {:>10} {:>10} MB\n", - proc.name, proc.pid, memory_mb - )); + + // Determine process type (this is a simplified approach) + let process_type = if proc.name.contains("comp") + || proc.name.contains("cuda") + || proc.name.contains("nvidia") + { + "C" // Compute + } else { + "G" // Graphics + }; + + // Alternate row colors for better readability + let row_prefix = if idx % 2 == 0 { " " } else { " " }; // Using spaces for alternating appearance + + // Highlight the currently selected/highlighted process + let line = if process_selection_enabled && idx == highlighted_process_index + { + format!( + "> {:<38} {:>15} {:>13} MB {}\n", + proc.name, proc.pid, memory_mb, process_type + ) + } else if selected_process_pid == Some(proc.pid) { + format!( + "* {:<38} {:>15} {:>13} MB {}\n", + proc.name, proc.pid, memory_mb, process_type + ) + } else { + format!( + "{}{:<38} {:>15} {:>13} MB {}\n", + row_prefix, proc.name, proc.pid, memory_mb, process_type + ) + }; + + content.push_str(&line); } content } @@ -567,8 +778,10 @@ fn fuzzy_score(text: &str, pattern: &str) -> i32 { while text_idx < text_chars.len() && pattern_idx < pattern_chars.len() { if text_chars[text_idx] == pattern_chars[pattern_idx] { // Bonus for consecutive matches - if text_idx > 0 && pattern_idx > 0 && - text_chars[text_idx - 1] == pattern_chars[pattern_idx - 1] { + if text_idx > 0 + && pattern_idx > 0 + && text_chars[text_idx - 1] == pattern_chars[pattern_idx - 1] + { consecutive_bonus += 10; } else { consecutive_bonus = 10; // Base bonus for a match From dcd3794c915771a22f606f7e73b5463959eabe84 Mon Sep 17 00:00:00 2001 From: jer Date: Tue, 20 Jan 2026 16:04:46 +1100 Subject: [PATCH 04/10] pre cleanups --- src/app.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app.rs b/src/app.rs index b4aa57d..f15e441 100644 --- a/src/app.rs +++ b/src/app.rs @@ -215,7 +215,7 @@ pub fn run( let search_text = format!("{}_", &fuzzy_search_input); let block = Block::default() .borders(Borders::ALL) - .title("🔍 Fuzzy Search - Type to search (ESC to cancel)") + .title("🔍... [ESC to cancel]") .border_style(Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)); let search_input = Paragraph::new(search_text) @@ -478,7 +478,7 @@ fn draw_misc<'d>(gpu: &'d GpuInfo<'d>) -> Paragraph<'d> { // Function to draw the driver info panel fn draw_driver_info<'d>(gpu: &GpuInfo<'d>) -> Paragraph<'d> { let block = Block::default().borders(Borders::ALL).title(Span::styled( - "Driver Info", + "Card Info", Style::default() .fg(Color::Blue) .add_modifier(Modifier::BOLD), From 854f6b30e21c4f07e79e3a0cf069616651eb026f Mon Sep 17 00:00:00 2001 From: jer Date: Tue, 20 Jan 2026 16:14:39 +1100 Subject: [PATCH 05/10] pre cleanups --- src/app.rs | 78 ++++++------------------------------------------------ 1 file changed, 8 insertions(+), 70 deletions(-) diff --git a/src/app.rs b/src/app.rs index f15e441..0452d6b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -44,12 +44,12 @@ pub fn run( .any(|gpu| gpu.inner.num_fans().map_or(0, |fc| fc) != 0); // State variables for process view - let mut show_process_view: bool = true; // Always show process view as requested + let mut show_process_view: bool = true; let mut fuzzy_search_active: bool = false; let mut fuzzy_search_input: String = String::new(); let mut sort_by: ProcessSortBy = ProcessSortBy::Memory; - let mut sort_reverse: bool = true; // Descending by default + let mut sort_reverse: bool = true; // State for process selection let mut selected_process_pid: Option = None; @@ -142,13 +142,12 @@ pub fn run( .style(Style::default()); f.render_widget(block, mid_area); - // Main vertical layout: top for card info, middle for metrics, bottom for processes let main_layout = Layout::default() .direction(Direction::Vertical) .constraints([ - Constraint::Length(3), // Top 3 lines for card info - Constraint::Percentage(50), // Middle 50% for metrics - Constraint::Percentage(45), // Bottom 45% for processes + Constraint::Length(3), + Constraint::Percentage(50), + Constraint::Percentage(45), ]) .margin(1) .split(f.area()); @@ -460,22 +459,7 @@ fn draw_memory_usage<'d>(gpu: &GpuInfo<'d>) -> Gauge<'d> { .ratio(mem_percentage) } -fn draw_misc<'d>(gpu: &'d GpuInfo<'d>) -> Paragraph<'d> { - let block = Block::default().borders(Borders::ALL).title(Span::styled( - "Misc", - Style::default() - .fg(Color::Magenta) - .add_modifier(Modifier::BOLD), - )); - let spanned_label = Span::styled(&gpu.misc, Style::new().white().bold()); - - Paragraph::new(spanned_label) - .block(block) - .wrap(Wrap { trim: true }) -} - -// Function to draw the driver info panel fn draw_driver_info<'d>(gpu: &GpuInfo<'d>) -> Paragraph<'d> { let block = Block::default().borders(Borders::ALL).title(Span::styled( "Card Info", @@ -490,7 +474,7 @@ fn draw_driver_info<'d>(gpu: &GpuInfo<'d>) -> Paragraph<'d> { }; let info_text = format!( - "Card: {}\nDriver: {}\nCUDA: {:.1}\nCompute Cap: {}", + "Card: {:<15} | Driver: {:<12} | CUDA: {:<8} | Compute Cap: {}", gpu.card_type, gpu.driver_version, gpu.cuda_version / 1000.0, @@ -503,53 +487,7 @@ fn draw_driver_info<'d>(gpu: &GpuInfo<'d>) -> Paragraph<'d> { } // Helper function to get process-specific information -fn get_process_specific_info<'d>(gpu: &GpuInfo<'d>, pid: u32) -> String { - match get_gpu_processes(gpu) { - Ok(processes) => { - if let Some(proc) = processes.iter().find(|p| p.pid == pid) { - format!( - "PID: {}\nProcess: {}\nMemory: {} MB", - proc.pid, - proc.name, - proc.used_memory / 1024 / 1024 - ) - } else { - format!("PID: {}\nProcess: Unknown\nMemory: N/A", pid) - } - } - Err(_) => { - format!("PID: {}\nProcess: Error retrieving info", pid) - } - } -} -// Function to draw process-specific info -fn draw_process_info<'d>(_gpu: &GpuInfo<'d>, process_info: &'d str) -> Paragraph<'d> { - let block = Block::default().borders(Borders::ALL).title(Span::styled( - "Selected Process", - Style::default() - .fg(Color::Cyan) - .add_modifier(Modifier::BOLD), - )); - - Paragraph::new(process_info) - .block(block) - .wrap(Wrap { trim: true }) -} - -// Function to draw process-specific clock info -fn draw_process_clock_info<'d>(_gpu: &GpuInfo<'d>, process_info: &'d str) -> Paragraph<'d> { - let block = Block::default().borders(Borders::ALL).title(Span::styled( - "Process Details", - Style::default() - .fg(Color::Cyan) - .add_modifier(Modifier::BOLD), - )); - - Paragraph::new(process_info) - .block(block) - .wrap(Wrap { trim: true }) -} fn draw_misc_with_processes<'d>( gpu: &GpuInfo<'d>, @@ -647,8 +585,8 @@ fn draw_misc_with_processes<'d>( "G" // Graphics }; - // Alternate row colors for better readability - let row_prefix = if idx % 2 == 0 { " " } else { " " }; // Using spaces for alternating appearance + // Row prefix for display + let row_prefix = " "; // Using spaces for appearance // Highlight the currently selected/highlighted process let line = if process_selection_enabled && idx == highlighted_process_index From bece8b16b3771bee17a5176cb9d5422211432e99 Mon Sep 17 00:00:00 2001 From: jer Date: Wed, 21 Jan 2026 08:44:02 +1100 Subject: [PATCH 06/10] chore: spelling --- README.md | 4 ++-- src/app.rs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fbbf064..94af9ea 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ _art by stable-diffusion + Maz_ - [troubleshooting](#troubleshooting) `nvtop` is a command-line utility that provides a replacement for some of the output from `nvidia-smi` (System Management Interface). -It offers real-time monitoring and visualization of GPU information: Core Clock, Temps, Fanspeed and Memory Usage. +It offers real-time monitoring and visualisation of GPU information: Core Clock, Temps, Fanspeed and Memory Usage. ______________________________________________________________________ @@ -154,6 +154,6 @@ ______________________________________________________________________ ### Advice for contributors: - if you touch the readme, please format it with `mdformat` (`pip install mdformat`). -- if you touch python scrpits, please format them with `black` (`pip install black`). +- if you touch python scripts, please format them with `black` (`pip install black`). - always run these `cargo test`, `cargo check`, `cargo clippy` -- please don't make PRs until any issues those tools flag are resolved. - if this is your first time contributing to open source, _wow! thank you_. diff --git a/src/app.rs b/src/app.rs index 0452d6b..a3e8d6f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -131,7 +131,7 @@ pub fn run( layout[1] }; - // Outermost Block, which draws the green border aound the whole UI. + // Outermost Block, which draws the green border around the whole UI. let block = Block::default() .title("NVTOP") .title_top(Line::from("NVTOP")) @@ -169,12 +169,12 @@ pub fn run( let left_middle = Layout::default() .constraints([ - Constraint::Percentage(50), // Core utilization + Constraint::Percentage(50), // Core utilisation Constraint::Percentage(50), // Core Clock ]) .split(middle_chunks[0]); - // Core utilization + // Core utilisation let core_gauge = draw_core_utilisation(gpu); f.render_widget(core_gauge, left_middle[0]); @@ -209,7 +209,7 @@ pub fn run( let process_widget = draw_misc_with_processes(gpu, &fuzzy_search_input, &sort_by, sort_reverse, process_selection_enabled, highlighted_process_index, selected_process_pid); f.render_widget(process_widget, main_layout[2]); - // If fuzzy search is active, render a modal-style search box in the center as an overlay + // If fuzzy search is active, render a modal-style search box in the Center as an overlay if fuzzy_search_active { let search_text = format!("{}_", &fuzzy_search_input); let block = Block::default() @@ -222,7 +222,7 @@ pub fn run( .style(Style::default().bg(Color::Rgb(40, 40, 40)).fg(Color::LightCyan).add_modifier(Modifier::BOLD)) .alignment(Alignment::Left); - // Calculate centered position for the search box + // Calculate Centerd position for the search box let search_width = 60.min(f.area().width.saturating_sub(2)); let search_height = 3; let x = f.area().width / 2 - search_width / 2; From b941dfb5a7984a0d78660d43935be1f00d737cad Mon Sep 17 00:00:00 2001 From: jer Date: Wed, 21 Jan 2026 08:45:13 +1100 Subject: [PATCH 07/10] chore: bump Edition, bump version --- Cargo.lock | 2 +- Cargo.toml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87e77bc..4f3b48a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1186,7 +1186,7 @@ dependencies = [ [[package]] name = "nvtop" -version = "0.1.2" +version = "0.1.3" dependencies = [ "anyhow", "clap 4.5.54", diff --git a/Cargo.toml b/Cargo.toml index 00f6d4b..f05d9d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "nvtop" -version = "0.1.2" -edition = "2021" +version = "0.1.3" +edition = "2024" description = """ nvtop: An NVIDIA SMI'esk GPU Monitoring tool for your terminal. """ keywords = [ "Real-time monitoring", @@ -12,7 +12,7 @@ keywords = [ "Command-line tool", ] authors = ["jer, alphastrata@gmail.com", "camuward, cameronuward@gmail.com"] -documentation = "https://www.github.com/alphastrata/nvtop" +documentation = "https://www.github.com/alphastrata/nvtop-rust" license = "MIT" homepage = "https://www.jeremyfwebb.ninja" publish = true From 0d380242b95fcca220014da198d0ed1ea7e8c8ac Mon Sep 17 00:00:00 2001 From: jer Date: Wed, 21 Jan 2026 08:51:08 +1100 Subject: [PATCH 08/10] chore: cleanups --- src/app.rs | 311 ++++++++++++++++++++++++----------------------------- src/gpu.rs | 4 +- 2 files changed, 144 insertions(+), 171 deletions(-) diff --git a/src/app.rs b/src/app.rs index a3e8d6f..fa80a8a 100644 --- a/src/app.rs +++ b/src/app.rs @@ -25,6 +25,13 @@ enum ProcessSortBy { Pid, } +#[derive(Debug)] +struct GpuProcess { + pid: u32, + used_memory: u64, + name: String, +} + pub fn run( nvml: nvml_wrapper::Nvml, delay: Duration, @@ -222,162 +229,155 @@ pub fn run( .style(Style::default().bg(Color::Rgb(40, 40, 40)).fg(Color::LightCyan).add_modifier(Modifier::BOLD)) .alignment(Alignment::Left); - // Calculate Centerd position for the search box + // Centre the search box let search_width = 60.min(f.area().width.saturating_sub(2)); let search_height = 3; let x = f.area().width / 2 - search_width / 2; - let y = f.area().height / 2 - 1; // Center vertically + let y = f.area().height / 2 - 1; // Vertically.. let search_area = Rect::new(x, y, search_width, search_height); // Draw a semi-transparent overlay background let overlay = Paragraph::new(" ") - .style(Style::default().bg(Color::Rgb(20, 20, 20))); // Very dark semi-transparent + .style(Style::default().bg(Color::Rgb(20, 20, 20))); // Very dark semi-transparent? f.render_widget(overlay, f.area()); f.render_widget(search_input, search_area); } })?; - if crossterm::event::poll(std::time::Duration::from_millis(250))? { - if let crossterm::event::Event::Key(key) = crossterm::event::read()? { - use crossterm::event::KeyCode; - - match key.code { - KeyCode::Char('q') => break, - KeyCode::Esc => { - // Exit fuzzy search if active, otherwise exit process view mode if currently in it - if fuzzy_search_active { - fuzzy_search_active = false; - fuzzy_search_input.clear(); - } else if show_process_view { - show_process_view = false; - } - } - KeyCode::Char('f') => { - // Always activate fuzzy search when 'f' is pressed - show_process_view = true; - fuzzy_search_active = true; + if crossterm::event::poll(std::time::Duration::from_millis(250))? + && let crossterm::event::Event::Key(key) = crossterm::event::read()? + { + use crossterm::event::KeyCode; + + match key.code { + KeyCode::Char('q') => break, + KeyCode::Esc => { + // Exit fuzzy search if active, otherwise exit process view mode if currently in it + if fuzzy_search_active { + fuzzy_search_active = false; fuzzy_search_input.clear(); + } else if show_process_view { + show_process_view = false; } - KeyCode::Char('/') => { - // Always activate fuzzy search when '/' is pressed - show_process_view = true; + } + KeyCode::Char('f') | KeyCode::Char('/') => { + // Always activate fuzzy search when '/' is pressed + show_process_view = true; + fuzzy_search_active = true; + fuzzy_search_input.clear(); + } + // Handle F keys for fuzzy search + KeyCode::F(1) + | KeyCode::F(2) + | KeyCode::F(3) + | KeyCode::F(4) + | KeyCode::F(5) + | KeyCode::F(6) + | KeyCode::F(7) + | KeyCode::F(8) + | KeyCode::F(9) + | KeyCode::F(10) + | KeyCode::F(11) + | KeyCode::F(12) => { + if !fuzzy_search_active { fuzzy_search_active = true; fuzzy_search_input.clear(); } - // Handle F keys for fuzzy search - KeyCode::F(1) - | KeyCode::F(2) - | KeyCode::F(3) - | KeyCode::F(4) - | KeyCode::F(5) - | KeyCode::F(6) - | KeyCode::F(7) - | KeyCode::F(8) - | KeyCode::F(9) - | KeyCode::F(10) - | KeyCode::F(11) - | KeyCode::F(12) => { - if !fuzzy_search_active { - fuzzy_search_active = true; - fuzzy_search_input.clear(); - } - } - // Handle character input for fuzzy search - but exclude 'f' and '/' which are handled separately - KeyCode::Char(c) if fuzzy_search_active => { - fuzzy_search_input.push(c); - } - // Handle backspace in fuzzy search - KeyCode::Backspace if fuzzy_search_active => { - fuzzy_search_input.pop(); - } - // Handle Enter to exit fuzzy search - KeyCode::Enter if fuzzy_search_active => { - fuzzy_search_active = false; - } - // Sorting controls - KeyCode::Char('s') => { - // Cycle through sorting options: Memory -> Name -> PID -> Memory... - sort_by = match sort_by { - ProcessSortBy::Memory => ProcessSortBy::Name, - ProcessSortBy::Name => ProcessSortBy::Pid, - ProcessSortBy::Pid => ProcessSortBy::Memory, - }; - } - KeyCode::Char('r') => { - // Reverse sort order - sort_reverse = !sort_reverse; - } - // Process selection controls - KeyCode::Char('p') if show_process_view && !fuzzy_search_active => { - // Toggle process selection mode - process_selection_enabled = !process_selection_enabled; - if !process_selection_enabled { - selected_process_pid = None; - } - } - KeyCode::Char(' ') if process_selection_enabled && show_process_view => { - // Select the currently highlighted process - if let Ok(processes) = get_gpu_processes(&gpu_list[selected_gpu]) { - if highlighted_process_index < processes.len() { - if let Some(proc) = processes.get(highlighted_process_index) { - selected_process_pid = Some(proc.pid); - process_selection_enabled = false; - } - } - } - } - KeyCode::Up if process_selection_enabled && show_process_view => { - // Move highlight up - highlighted_process_index = highlighted_process_index.saturating_sub(1); + } + // Handle character input for fuzzy search - but exclude 'f' and '/' which are handled separately + KeyCode::Char(c) if fuzzy_search_active => { + fuzzy_search_input.push(c); + } + // Handle backspace in fuzzy search + KeyCode::Backspace if fuzzy_search_active => { + fuzzy_search_input.pop(); + } + // Handle Enter to exit fuzzy search + KeyCode::Enter if fuzzy_search_active => { + fuzzy_search_active = false; + } + // Sorting controls + KeyCode::Char('s') => { + // Cycle through sorting options: Memory -> Name -> PID -> Memory... + sort_by = match sort_by { + ProcessSortBy::Memory => ProcessSortBy::Name, + ProcessSortBy::Name => ProcessSortBy::Pid, + ProcessSortBy::Pid => ProcessSortBy::Memory, + }; + } + KeyCode::Char('r') => { + // Reverse sort order + sort_reverse = !sort_reverse; + } + // Process selection controls + KeyCode::Char('p') if show_process_view && !fuzzy_search_active => { + // Toggle process selection mode + process_selection_enabled = !process_selection_enabled; + if !process_selection_enabled { + selected_process_pid = None; } - KeyCode::Down if process_selection_enabled && show_process_view => { - // Move highlight down - if let Ok(processes) = get_gpu_processes(&gpu_list[selected_gpu]) { - if highlighted_process_index < processes.len().saturating_sub(1) { - highlighted_process_index += 1; - } - } + } + KeyCode::Char(' ') if process_selection_enabled && show_process_view => { + // Select the currently highlighted process + if let Ok(processes) = get_gpu_processes(&gpu_list[selected_gpu]) + && highlighted_process_index < processes.len() + && let Some(proc) = processes.get(highlighted_process_index) + { + selected_process_pid = Some(proc.pid); + process_selection_enabled = false; } - KeyCode::F(n) - if (1..=gpu_list.len()).contains(&n.into()) && !fuzzy_search_active => + } + KeyCode::Up if process_selection_enabled && show_process_view => { + // Move highlight up + highlighted_process_index = highlighted_process_index.saturating_sub(1); + } + KeyCode::Down if process_selection_enabled && show_process_view => { + // Move highlight down + if let Ok(processes) = get_gpu_processes(&gpu_list[selected_gpu]) + && highlighted_process_index < processes.len().saturating_sub(1) { - selected_gpu = usize::from(n - 1) + highlighted_process_index += 1; } + } + KeyCode::F(n) + if (1..=gpu_list.len()).contains(&n.into()) && !fuzzy_search_active => + { + selected_gpu = usize::from(n - 1) + } - #[cfg(target_os = "linux")] - KeyCode::Char('p') => { - // re-scan pci tree to let driver discover new devices (only works as sudo) - match nvml.discover_gpus(PciInfo { - bus: 0, - bus_id: "".into(), - device: 0, - domain: 0, - pci_device_id: 0, - pci_sub_system_id: Some(0), - }) { - Ok(()) => { - have_fans = gpu_list - .iter() - .any(|gpu| gpu.inner.num_fans().map_or(0, |fc| fc) != 0); - - lh.debug(&format!("GPU has fans = {}", have_fans)); - lh.debug("Re-scanned PCI tree"); - } - Err(e @ (NvmlError::OperatingSystem | NvmlError::NoPermission)) => { - lh.debug(&format!("Failed to re-scan PCI tree: {e}")); - } - Err(e) => return Err(e.into()), + #[cfg(target_os = "linux")] + KeyCode::Char('p') => { + // re-scan pci tree to let driver discover new devices (only works as sudo) + match nvml.discover_gpus(PciInfo { + bus: 0, + bus_id: "".into(), + device: 0, + domain: 0, + pci_device_id: 0, + pci_sub_system_id: Some(0), + }) { + Ok(()) => { + have_fans = gpu_list + .iter() + .any(|gpu| gpu.inner.num_fans().map_or(0, |fc| fc) != 0); + + lh.debug(&format!("GPU has fans = {}", have_fans)); + lh.debug("Re-scanned PCI tree"); } - // re-scan for devices - gpu_list = crate::gpu::try_init_gpus(&nvml, lh)?; - if selected_gpu >= gpu_list.len() { - selected_gpu = 0; + Err(e @ (NvmlError::OperatingSystem | NvmlError::NoPermission)) => { + lh.debug(&format!("Failed to re-scan PCI tree: {e}")); } + Err(e) => return Err(e.into()), + } + // re-scan for devices + gpu_list = crate::gpu::try_init_gpus(&nvml, lh)?; + if selected_gpu >= gpu_list.len() { + selected_gpu = 0; } - _ => {} } + _ => {} } } @@ -459,7 +459,6 @@ fn draw_memory_usage<'d>(gpu: &GpuInfo<'d>) -> Gauge<'d> { .ratio(mem_percentage) } - fn draw_driver_info<'d>(gpu: &GpuInfo<'d>) -> Paragraph<'d> { let block = Block::default().borders(Borders::ALL).title(Span::styled( "Card Info", @@ -486,9 +485,6 @@ fn draw_driver_info<'d>(gpu: &GpuInfo<'d>) -> Paragraph<'d> { .wrap(Wrap { trim: true }) } -// Helper function to get process-specific information - - fn draw_misc_with_processes<'d>( gpu: &GpuInfo<'d>, search_term: &str, @@ -568,12 +564,11 @@ fn draw_misc_with_processes<'d>( if processes.is_empty() && !search_term.is_empty() { format!("No processes match '{}'", search_term) } else { - // Show only processes without redundant header let mut content = String::new(); + //TODO: Nicer stringing without these manual spaces... content.push_str("Name PID Memory(MB) Type\n"); - for (idx, proc) in processes.iter().take(20).enumerate() { - // Show top 20 processes - let memory_mb = proc.used_memory / 1024 / 1024; // Convert bytes to MB + for (idx, proc) in processes.iter().take(40).enumerate() { + let memory_mb = proc.used_memory / 1024 / 1024; // Determine process type (this is a simplified approach) let process_type = if proc.name.contains("comp") @@ -589,6 +584,7 @@ fn draw_misc_with_processes<'d>( let row_prefix = " "; // Using spaces for appearance // Highlight the currently selected/highlighted process + //BUG: this doesn't work... let line = if process_selection_enabled && idx == highlighted_process_index { format!( @@ -690,7 +686,7 @@ fn fuzzy_match(text: &str, pattern: &str) -> bool { pattern_idx == pattern_chars.len() } -// Helper function to score fuzzy matches (higher score = better match) +// Helper to score fuzzy matches (higher score = better match) fn fuzzy_score(text: &str, pattern: &str) -> i32 { if pattern.is_empty() { return 0; @@ -703,7 +699,7 @@ fn fuzzy_score(text: &str, pattern: &str) -> i32 { return -1; // No match } - // Calculate score based on match quality + // Calculate match quality let mut score = 0; let text_chars: Vec = text_lower.chars().collect(); let pattern_chars: Vec = pattern_lower.chars().collect(); @@ -737,27 +733,16 @@ fn fuzzy_score(text: &str, pattern: &str) -> i32 { score } -// Structure to represent a process running on the GPU -#[derive(Debug)] -struct GpuProcess { - pid: u32, - used_memory: u64, - name: String, // Process name -} - -// Function to retrieve processes running on the GPU fn get_gpu_processes<'d>(gpu: &GpuInfo<'d>) -> Result, NvmlError> { let mut gpu_processes = Vec::new(); - // Try to get compute processes if let Ok(compute_processes) = gpu.inner.running_compute_processes() { for process in compute_processes { let name = match std::fs::read_to_string(format!("/proc/{}/comm", process.pid)) { Ok(name) => name.trim().to_string(), - Err(_) => format!("Process-{}", process.pid), // Fallback if we can't get the name + Err(_) => format!("Process-{}", process.pid), }; - // Access memory value for compute processes - it's a direct UsedGpuMemory value let memory_value = extract_memory_value(process.used_gpu_memory); gpu_processes.push(GpuProcess { @@ -768,17 +753,14 @@ fn get_gpu_processes<'d>(gpu: &GpuInfo<'d>) -> Result, NvmlError } } - // Also try to get graphics processes to get a complete picture if let Ok(graphics_processes) = gpu.inner.running_graphics_processes() { for process in graphics_processes { - // Check if this process is already in our list (from compute processes) if !gpu_processes.iter().any(|p| p.pid == process.pid) { let name = match std::fs::read_to_string(format!("/proc/{}/comm", process.pid)) { Ok(name) => name.trim().to_string(), - Err(_) => format!("Process-{}", process.pid), // Fallback if we can't get the name + Err(_) => format!("Process-{}", process.pid), }; - // Extract memory value for graphics process - based on the error, it's UsedGpuMemory (not optional) let memory_value = extract_memory_value(process.used_gpu_memory); gpu_processes.push(GpuProcess { @@ -793,25 +775,16 @@ fn get_gpu_processes<'d>(gpu: &GpuInfo<'d>) -> Result, NvmlError Ok(gpu_processes) } -// Helper function to extract the memory value from UsedGpuMemory enum -// Using a direct approach with the actual enum structure fn extract_memory_value(used_memory: nvml_wrapper::enums::device::UsedGpuMemory) -> u64 { - // Since we can't determine the exact API, let's use a different approach - // by converting the enum to a string and extracting the value let s = format!("{:?}", used_memory); - // Look for the pattern where the memory value appears in the debug output - // For example, if it looks like "Unrestricted(123456)" or similar - if let Some(start) = s.find('(') { - if let Some(end) = s.find(')') { - if start < end { - let num_str = &s[start + 1..end]; - return num_str.parse::().unwrap_or(0); - } - } + if let Some(start) = s.find('(') + && let Some(end) = s.find(')') + && start < end + { + let num_str = &s[start + 1..end]; + return num_str.parse::().unwrap_or(0); } - - // If the pattern isn't found, try to extract any number from the string s.chars() .filter(|c| c.is_ascii_digit()) .collect::() diff --git a/src/gpu.rs b/src/gpu.rs index aaa13a6..bf25bc3 100644 --- a/src/gpu.rs +++ b/src/gpu.rs @@ -5,9 +5,9 @@ use std::{ }; use nvml_wrapper::{ + Device, Nvml, enum_wrappers::device::{Clock, ClockId, TemperatureSensor}, error::NvmlError, - Device, Nvml, }; use crate::{errors::NvTopError, termite::LoggingHandle}; @@ -140,8 +140,8 @@ pub fn try_init_gpus<'n>( #[cfg(test)] mod tests { use nvml_wrapper::{ - enum_wrappers::device::{Clock, ClockId}, Nvml, + enum_wrappers::device::{Clock, ClockId}, }; #[ignore = ""] From 965b3cf86bb391f2f7e99db0e523558268a5e5c6 Mon Sep 17 00:00:00 2001 From: jer Date: Wed, 21 Jan 2026 08:51:57 +1100 Subject: [PATCH 09/10] chore: remove redundant comments.. --- src/termite.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/termite.rs b/src/termite.rs index aa878a8..859da9c 100644 --- a/src/termite.rs +++ b/src/termite.rs @@ -92,13 +92,11 @@ impl LoggingHandle { } fn write_to_log(log_path: &Path, msg: &str) { - // Open the log file in append mode, creating it if it doesn't exist let mut file = OpenOptions::new() .create(true) .append(true) .open(log_path).unwrap_or_else(|_| panic!("Unable to create {}.\nAs a result of this we cannot write logs. So the app will crash.", log_path.display())); - // Write the message to the log file file.write_all(msg.as_bytes()) .expect("Unable to write log to file -- We chose to exit the app when this happens."); } From 4f9b35d759ea14f25a50d9d7c4846693e01557c4 Mon Sep 17 00:00:00 2001 From: jer Date: Wed, 21 Jan 2026 09:03:46 +1100 Subject: [PATCH 10/10] add hotkeys to readme. --- README.md | 13 +++++++++++++ src/app.rs | 27 ++------------------------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 94af9ea..8537e6b 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,19 @@ nvtop nvtop --log ``` +
+### Keyboard Shortcuts: + +| Shortcut | Functionality | +|----------|---------------| +| `f` or `/` | Search processes (fuzzy) | +| `s` | 'Sort' [Name || Pid || Memory Usage] | +| `p` | Processes (up / down arrows) | +| `r` | Reverse (processes list ordering) | +| `m` | Multi-Gpu Rescan PCIe devices (Linux Only, multi-gpu systems only.) | +| `q` | Quit | + +
______________________________________________________________________ ### Prerequisites diff --git a/src/app.rs b/src/app.rs index fa80a8a..a611db5 100644 --- a/src/app.rs +++ b/src/app.rs @@ -268,24 +268,6 @@ pub fn run( fuzzy_search_active = true; fuzzy_search_input.clear(); } - // Handle F keys for fuzzy search - KeyCode::F(1) - | KeyCode::F(2) - | KeyCode::F(3) - | KeyCode::F(4) - | KeyCode::F(5) - | KeyCode::F(6) - | KeyCode::F(7) - | KeyCode::F(8) - | KeyCode::F(9) - | KeyCode::F(10) - | KeyCode::F(11) - | KeyCode::F(12) => { - if !fuzzy_search_active { - fuzzy_search_active = true; - fuzzy_search_input.clear(); - } - } // Handle character input for fuzzy search - but exclude 'f' and '/' which are handled separately KeyCode::Char(c) if fuzzy_search_active => { fuzzy_search_input.push(c); @@ -341,14 +323,9 @@ pub fn run( highlighted_process_index += 1; } } - KeyCode::F(n) - if (1..=gpu_list.len()).contains(&n.into()) && !fuzzy_search_active => - { - selected_gpu = usize::from(n - 1) - } - + #[cfg(target_os = "linux")] - KeyCode::Char('p') => { + KeyCode::Char('m') => { // re-scan pci tree to let driver discover new devices (only works as sudo) match nvml.discover_gpus(PciInfo { bus: 0,