process_viewer-0.5.9/.cargo_vcs_info.json0000644000000001360000000000100141060ustar { "git": { "sha1": "0c088b286d6881e900824eec74fb2562768ef652" }, "path_in_vcs": "" }process_viewer-0.5.9/.github/FUNDING.yml000064400000000000000000000002121046102023000160460ustar 00000000000000# These are supported funding model platforms github: [GuillaumeGomez] patreon: GuillaumeGomez custom: ["https://paypal.me/imperioland"] process_viewer-0.5.9/.github/workflows/CI.yml000064400000000000000000000024001046102023000173050ustar 00000000000000on: push: branches: [master] pull_request: name: CI jobs: rustfmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true components: rustfmt - run: cargo fmt --all -- --check build: runs-on: ${{ matrix.os }} strategy: matrix: rust: - stable - beta - nightly - "1.70" # Minimum supported version (from gtk-rs) os: - ubuntu-22.04 - macos-latest steps: - run: sudo apt-get update -y if: matrix.os == 'ubuntu-22.04' - run: sudo apt-get install -y libgtk-4-dev curl libcairo-gobject2 libcairo2-dev if: matrix.os == 'ubuntu-22.04' - run: brew update if: matrix.os == 'macos-latest' - run: brew install gtk4 cairo atk if: matrix.os == 'macos-latest' - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} os: $${{ matrix.os }} components: clippy override: true - name: compile run: cargo build - name: clippy run: cargo clippy process_viewer-0.5.9/.gitignore000064400000000000000000000000761046102023000146710ustar 00000000000000*target **.dll **-config flatpak/.flatpak-builder flatpak/repoprocess_viewer-0.5.9/Cargo.lock0000644000000604030000000000100120640ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "anyhow" version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "async-channel" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" dependencies = [ "concurrent-queue", "event-listener", "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] name = "autocfg" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "cairo-rs" version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ac2a4d0e69036cf0062976f6efcba1aaee3e448594e6514bb2ddf87acce562" dependencies = [ "bitflags", "cairo-sys-rs", "glib", "libc", "thiserror", ] [[package]] name = "cairo-sys-rs" version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3bb3119664efbd78b5e6c93957447944f16bdbced84c17a9f41c7829b81e64" dependencies = [ "glib-sys", "libc", "system-deps", ] [[package]] name = "cfg-expr" version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" dependencies = [ "smallvec", "target-lexicon", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "concurrent-queue" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] [[package]] name = "core-foundation-sys" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "crossbeam-deque" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 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-utils" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "either" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "event-listener" version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] [[package]] name = "event-listener-strategy" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" dependencies = [ "event-listener", "pin-project-lite", ] [[package]] name = "field-offset" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ "memoffset", "rustc_version", ] [[package]] name = "futures-channel" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", ] [[package]] name = "futures-core" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", "syn 2.0.60", ] [[package]] name = "futures-task" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "gdk-pixbuf" version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6a23f8a0b5090494fd04924662d463f8386cc678dd3915015a838c1a3679b92" dependencies = [ "gdk-pixbuf-sys", "gio", "glib", "libc", ] [[package]] name = "gdk-pixbuf-sys" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcbd04c1b2c4834cc008b4828bc917d062483b88d26effde6342e5622028f96" dependencies = [ "gio-sys", "glib-sys", "gobject-sys", "libc", "system-deps", ] [[package]] name = "gdk4" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9100b25604183f2fd97f55ef087fae96ab4934d7215118a35303e422688e6e4b" dependencies = [ "cairo-rs", "gdk-pixbuf", "gdk4-sys", "gio", "glib", "libc", "pango", ] [[package]] name = "gdk4-sys" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0b76874c40bb8d1c7d03a7231e23ac75fa577a456cd53af32ec17ec8f121626" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", "gio-sys", "glib-sys", "gobject-sys", "libc", "pango-sys", "pkg-config", "system-deps", ] [[package]] name = "gio" version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f91a0518c2ec539f099d3f945ab2d6a83ec372a9ef40a21906343b191182845" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-util", "gio-sys", "glib", "libc", "pin-project-lite", "smallvec", "thiserror", ] [[package]] name = "gio-sys" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcf8e1d9219bb294636753d307b030c1e8a032062cba74f493c431a5c8b81ce4" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", "windows-sys", ] [[package]] name = "glib" version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1407b2ce171e654720be10d57d4054d3ff2f10a13d5b37e6819b41439832f7" dependencies = [ "bitflags", "futures-channel", "futures-core", "futures-executor", "futures-task", "futures-util", "gio-sys", "glib-macros", "glib-sys", "gobject-sys", "libc", "memchr", "smallvec", "thiserror", ] [[package]] name = "glib-macros" version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8bba315e8ce8aa59631545358450f4962557e89b5f7db7442e7153b47037f71" dependencies = [ "heck", "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.60", ] [[package]] name = "glib-sys" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630f097773d7c7a0bb3258df4e8157b47dc98bbfa0e60ad9ab56174813feced4" dependencies = [ "libc", "system-deps", ] [[package]] name = "gobject-sys" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c85e2b1080b9418dd0c58b498da3a5c826030343e0ef07bde6a955d28de54979" dependencies = [ "glib-sys", "libc", "system-deps", ] [[package]] name = "graphene-rs" version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99e4d388e96c5f29e2b2f67045d229ddf826d0a8d6d282f94ed3b34452222c91" dependencies = [ "glib", "graphene-sys", "libc", ] [[package]] name = "graphene-sys" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "236ed66cc9b18d8adf233716f75de803d0bf6fc806f60d14d948974a12e240d0" dependencies = [ "glib-sys", "libc", "pkg-config", "system-deps", ] [[package]] name = "gsk4" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c65036fc8f99579e8cb37b12487969b707ab23ec8ab953682ff347cbd15d396e" dependencies = [ "cairo-rs", "gdk4", "glib", "graphene-rs", "gsk4-sys", "libc", "pango", ] [[package]] name = "gsk4-sys" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd24c814379f9c3199dc53e52253ee8d0f657eae389ab282c330505289d24738" dependencies = [ "cairo-sys-rs", "gdk4-sys", "glib-sys", "gobject-sys", "graphene-sys", "libc", "pango-sys", "system-deps", ] [[package]] name = "gtk4" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa82753b8c26277e4af1446c70e35b19aad4fb794a7b143859e7eeb9a4025d83" dependencies = [ "cairo-rs", "field-offset", "futures-channel", "gdk-pixbuf", "gdk4", "gio", "glib", "graphene-rs", "gsk4", "gtk4-macros", "gtk4-sys", "libc", "pango", ] [[package]] name = "gtk4-macros" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40300bf071d2fcd4c94eacc09e84ec6fe73129d2ceb635cf7e55b026b5443567" dependencies = [ "anyhow", "proc-macro-crate", "proc-macro-error", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "gtk4-sys" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0db1b104138f087ccdc81d2c332de5dd049b89de3d384437cc1093b17cd2da18" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", "gdk4-sys", "gio-sys", "glib-sys", "gobject-sys", "graphene-sys", "gsk4-sys", "libc", "pango-sys", "system-deps", ] [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indexmap" version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "memchr" version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "memoffset" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "ntapi" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" dependencies = [ "winapi", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "pango" version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1264d13deb823cc652f26cfe59afb1ec4b9db2a5bd27c41b738c879cc1bfaa1" dependencies = [ "gio", "glib", "libc", "pango-sys", ] [[package]] name = "pango-sys" version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52ef6a881c19fbfe3b1484df5cad411acaaba29dbec843941c3110d19f340ea" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", ] [[package]] name = "parking" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "proc-macro-crate" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ "toml_edit 0.21.1", ] [[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", "syn 1.0.109", "version_check", ] [[package]] name = "proc-macro-error-attr" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", "version_check", ] [[package]] name = "proc-macro2" version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] [[package]] name = "process_viewer" version = "0.5.9" dependencies = [ "async-channel", "gtk4", "libc", "serde", "serde_derive", "sysinfo", "toml", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] [[package]] name = "semver" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", "syn 2.0.60", ] [[package]] name = "serde_spanned" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[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.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "sysinfo" version = "0.30.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87341a165d73787554941cd5ef55ad728011566fe714e987d1b976c15dbc3a83" dependencies = [ "cfg-if", "core-foundation-sys", "libc", "ntapi", "once_cell", "rayon", "windows", ] [[package]] name = "system-deps" version = "6.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" dependencies = [ "cfg-expr", "heck", "pkg-config", "toml", "version-compare", ] [[package]] name = "target-lexicon" version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "thiserror" version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", "syn 2.0.60", ] [[package]] name = "toml" version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit 0.22.12", ] [[package]] name = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap", "toml_datetime", "winnow 0.5.40", ] [[package]] name = "toml_edit" version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow 0.6.6", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "version-compare" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core", "windows-targets", ] [[package]] name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] [[package]] name = "winnow" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" dependencies = [ "memchr", ] process_viewer-0.5.9/Cargo.toml0000644000000022100000000000100120770ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "process_viewer" version = "0.5.9" authors = ["Guillaume Gomez "] description = "A process viewer GUI" homepage = "https://github.com/GuillaumeGomez/process-viewer" readme = "README.md" keywords = [ "GUI", "process", "viewer", "gtk", ] license = "MIT" repository = "https://github.com/GuillaumeGomez/process-viewer" [dependencies.async-channel] version = "2.2.1" [dependencies.gtk] version = "0.8" package = "gtk4" [dependencies.libc] version = "0.2" [dependencies.serde] version = "1.0" [dependencies.serde_derive] version = "1.0" [dependencies.sysinfo] version = "0.30" [dependencies.toml] version = "0.8" process_viewer-0.5.9/Cargo.toml.orig000064400000000000000000000010331046102023000155620ustar 00000000000000[package] name = "process_viewer" version = "0.5.9" authors = ["Guillaume Gomez "] edition = "2021" description = "A process viewer GUI" repository = "https://github.com/GuillaumeGomez/process-viewer" license = "MIT" homepage = "https://github.com/GuillaumeGomez/process-viewer" readme = "README.md" keywords = ["GUI", "process", "viewer", "gtk"] [dependencies] gtk = { version = "0.8", package = "gtk4" } sysinfo = "0.30" libc = "0.2" serde = "1.0" serde_derive = "1.0" toml = "0.8" async-channel = "2.2.1" process_viewer-0.5.9/LICENSE000064400000000000000000000020731046102023000137050ustar 00000000000000The MIT License (MIT) Copyright (c) 2015 Guillaume Gomez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. process_viewer-0.5.9/README.md000064400000000000000000000030711046102023000141560ustar 00000000000000# process-viewer [![Build Status](https://travis-ci.org/GuillaumeGomez/process-viewer.png?branch=master)](https://travis-ci.org/GuillaumeGomez/process-viewer) A process viewer GUI in rust. It provides current status of your processes (cpu and memory usage) and your system (usage of every core and of your RAM, and the temperature of your components if this information is available). It can be run on the following platforms: * Linux * Raspberry * macOS * FreeBSD * Windows (for cross-compilation to Windows, you can give a try to https://hub.docker.com/r/etrombly/rust-crosscompile) Please run it in release mode to have good performance: ```bash cargo run --release ``` or to install it as binary ```bash cargo install process_viewer ``` ### Building/running on Linux, MacOS Take a look at the [gtk-rs installation guide](https://gtk-rs.org/gtk4-rs/stable/latest/book/installation.html) to know how install GTK dependencies. ### Running on Raspberry It'll be difficult to build on Raspberry pi directly. A good way-around is to be build on Linux before sending it to your Raspberry pi: ```bash rustup target add armv7-unknown-linux-gnueabihf cargo build --target=armv7-unknown-linux-gnueabihf ``` ## Donations If you appreciate my work and want to support me, you can do it here: [![Become a patron](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/GuillaumeGomez) ## Screenshots ![screenshot](http://guillaume-gomez.fr/image/process-viewer-screen1.png) ![screenshot](http://guillaume-gomez.fr/image/process-viewer-screen2.png) process_viewer-0.5.9/assets/eye.png000064400000000000000000001325051046102023000154760ustar 00000000000000PNG  IHDRݾPIDATxtdKw-Z=[k4@ 0c| d z*2##Yzd=Egˊq;{C~+/?λ-C*q.4nkiEqVũcR\)&t o'PO\=S1N_/7M]U<M<}c~0Ctl6͆;]0A<GƆ~m|ǁuhf3]o6<~Nlq:%2q~N/7ey-NS1[h/i=s9H<뻴rqtC MzS^[Jtr KƏAPeH_7x-_\աQ`B\H"3IEƘo%Ert23K%8V0(_ O:X) uS1}o_E70:=oMpzv:b%"S];S`0*2]66X+U Smr#\dpL't3$ tuv]֕i_Tmږ2>NU4!iV*z55I·XMݵ=/[,4A19:B"ZEJCmlM͹8UG+0}|#QϘw ,::>SU =W(떌uX3iփ |dbhlJu|^]ZWBm\P9}Dpfu 2^EHuMj&a&I,-빼'v7#E&4,yF8 nd]Uw]G%D2&=%\&b\UA̹7ieTǩ#t3Tӕb\\@z){Dao UbhDk$r`Jf0Sq& $ 64WԚ' M5ZdWJjY`f^H'm::)+_y,@˜O黉7 F tj$m(U*-Bj*DAzs3W 5(؆ODW<9$쉖W]OzU%A>{rG7;GG般 7|{df'!1/5a8i[B7,CǃW02[2NJT%h]ӶPaل$Ѵ?tDԉTNL߆ fb&e QMi%]Hg+Xtt<Ѧ0w+VUqOb;YitJ>]VLC ]DxbQ*,դ-oҵ.<` SxA\ !\`Jd֓jiS'40@t*2#K{@'>(*umaS7c첶35!"1D'‚Cj|0MKGzĘ"L `2F<٣80TM(RL DT$qy]qj(sD|Wx %ʓ3K-› <Ŏ\5W:vKHuf3xox](.ٌ Q24{X{1[͓/S9wKK̞^4[-5թaBWCed#c"8*uwʿoEa|UWR?/_"+HF!5^֮<6l;AF.zNr! |$EV U%ɸ%6i%ũ+ < HLWȹcڬp$AEYij0Oas`2׳aИT31 3iWPewYq= 5;XrD}Vz!TpU-f K-~ =$c}CA|V[ĝ~gs|fJy Y(l1 \>Rӹ> =UTo_*FO毎N6Y2Y |o'g:c+8y $g*T@`&EG9g#3& V+-gbLGv ~owYWLTյΪ2g n7ו(RƧ qLq/)g ”%*MXpA*xYԯďJUl\@Hž񟹣*s :%&Wp8T#e|ѴN]r4C߳0sSfCbF +R0#-tc p M t |Zuk`jZ3|P Q7Q`1T ގ'Ǻpj VfS<̰cdȪߖ֫,a#ȱ HtAUe舻2|vOȃ.%QI6 4"M'TL]20MpL9:%,Lj4@DP[-ȅcDrdw&CS[9xсEVC]]j8ӰE$:g j@gŬ[zF!-Wr`箨%P#X %,R'JtFH!rc'g/5a|R5Bg_$* PR~J'M +4﹌،)K dW#,u=! F*( Yb.3)_$EFKBglȣ$L< љeRs:&ĕf  ԅe}ATʍ+iVN5n[7-C&@ؙҺ43 z4J*{5+2b&T%.9~Jᝏ=6V@bIS>+lJ5#@ 5w8IGNXII$l6"58q*Oc%3$j΂8r,W1&˳meZ2gaSpd2+Wj Z`ܺk*<,cRFa},љ8@j2׵< 묤@]j_>m6K%aF#:aXG&a=3 pPb 9΄20dK,aQ(illk=HbTSձi`3RL>ɍ,sC(%]^0+.|^bM$WPTg]Ʌ=erV'Cn8;NW %Kר1K\|so>N ?{pPqAԿˮ-]PXRS㛑Α%\wq9W^1;J-йHzV#.Sɒ \2@Ac2#CA!m:|c^!|t^OV8esJp He! ($"Ts),'PQp4,oZŽU>H MϬ~NU&fL2xR/ĉLN:c.Tjy}|V [w Ce)4@O(a8V 7ju9QU|a]+gJ:8*9Cޯ*ZgR1\`)DnhNM1WHUj:ۉhBu2E\;{(%$3-nIwJBVS,Lk6dQxO2GHDTuһqi7rQmB|Jjɲ8<hK('l ec{?RfnIWѼ}l|9U/N宓Y*JTwNZ`?|xp ByZ}OGRwF`=C󱰵9I*rV ,cf=3k+ IB :Bi`Ax8q E ɰ<:v,{3KCTO>Kw/E x"ea.2i;MV[Xz[r\lQeR MfME_&Z_50%"A2Я%T\V^+<?.!']%wI>2 TIhF+ MJs$1'",$XrAʢbndW?=>:.9`s)2Qw*OULq><AByMD>YwBZy9&2p2\VGo<Y⨌K՚4" +?%C(OzJq-0 {WY*uytMJS^:=g$CǝF"MbʹUln۲7 {~!t{7j^u Pӗɖ;HfBO_u݆8@C EK:=x'4_E340x0GbcAgY ő/]_8UJJvY&ȘXRIp_GX$-@=Ĵ5/+xjN"Еc@^I8%'.&vw>~wS<=~Z t5zEt)mzL/0$83{c)Ě**D5_ Ԕ)9 ֞ xmF #eO51\tʴX?JxT?r|Zgq Y4éq ]:f jp P ʈf^\Rah"9!F34#hh(PI DěpXjVwM3\B,͑r<_GEIX=YE>ʹ5rRb1j-w#Tκ~x-T8zMy#UqB*|QgHqκIq}yRX3bSbVm[ *)ǠO~L\JgiI2sTJUU G"vSDd] }Or4HuM]oiE@$mytpu6BgtjRLlJd}(+[%pިB\i_S}RH/ddyjg0XSdH>WAFT'5_YYHJxsQI1%p)+4vp99%w$@Y$Mb@Eo6l^)䷐AC2Rp>6`vMGJkN(@svr:3` M?/}SD]L3&PǶ ; =Daf$qR'JƩ _vZ)EGӘj(&r($ Ni% b"W <-~M%#Q)w6/RV$RzpǙD#$l&Ӫ,W+bM䭅94(Hlv7#Ww-|a@~gYqYfYtt_G6!2\.̴~jwB*M#UsE}˗jwѮ0fuqݠ }埛qQ)ҷ_pI8J n10|aVuݢEM$ [[JMu__~/w2=?_z˯ݿw?>~M {Ɂ5R )6=HѠڨ͎ƀDԦnﶻ$d\ä' j#YLKh_NVt[nud1-R1Z0x&f49*n.WltNX=XxoBaqQHP *$kr#`Yd!$M=PݪM{2'o~a{v'e6OO?Ӥjjs!o`Ѕ_ $L|&vm=wA3=~ uG+7~~ʫr٥ěwfɨ@UAp5ׇϓ5Y*J%a-zS>}~WW8!bs#IȞuՔJY8q\CxdeW''֞03dh!knG/--PҠymo7_~ӿ-q_ˎK?^[ZO?_Fw>Ӈdyf= vQri Y%2BIJD5R' v̧S 8,&o9T*u 5's~ZVc/an, 9p$"q!1eUFKkO,,]{LPc⼘8cSQ΋f[PvE̓'C VMմ}jir01L_|{z8cwwWÇ~.knۧA 2 zn_/bsDz'aM8Nt2Y$nV?ΑU׸"K\6Ha%+U ,;Ɩ4ˍeg@V^?fiƲUk%<'b!V%re{*kqiԚqtF{JLhI dx `FZc*ת7þF+bWOGh#J&O_}qo0?}MI0A߮Ï0ГߠoK>` r%H@CQ+]J ~)l2![*%Ö] rWN+aYWrkkR>{kx1 %I'jSݑna '҃GP QKy'қeoGyDF>sҷHJR&\ՅZՅjkԭx kֆ k,<־e8Wʌ5[mK"e3s%&`B bo֐7"`4c'$LD6] Ddv7_bA/2|0pO];e BxN2eL/޾)"AH(A ϳs#E.Ιmh3f7J6ad>>~>vs~T[+wCYE5 ^LaFYfZb[2S R@zc,4 M@l)h[xM5u^/_W1ĭ'0N׶ h9z]06yݶm-Mǟ~![S74WdO?6p=^a'(sv&zTd..!v^%۹ǨWXI[VXW],Γ2(]PD\]A\V+aL{iPn^t=8uII9v)SӐa2DHOݖ FF?>|ik &XN}ݻwׯ_BeiK|J,:H5TEd~fG%垬4^%;]+^vU$y1-ԗ+ A^{Tq:u{zmI+rWʞRR+"$J[ZD&}9f0fv?& &-VA7Cv{w \`y 9tۻM3i@OϤ4qqD'|=E: S/vz>Ջpy8/?9EE>4wgr} vgb+6G֞}6/ $^_}A/5Uxf&+|r 5[MggcpQ^n[%OJV"U "-\}Ol [I?c?ͮؒuM >t|TuBErOdjybbӷ~.8GtK%FRiFFLᾼQN!bRc\u(x;'FOp̸Ylt"W'TLm2ltX  yߗ, 7 ǿQ'L?X>fϓy噐\}-]4lm4-fh"e6!/nFoAzǧ?!7o"O_8I/GZO$T&{diiD><^HUDU׃+2Y)L^2AQ{+_A˖B؉%9*"wUs-}:>0W )gFdSvQʟt+_{- ۤ# Db'Q$H$F fUɟ jql:_l_GD;7 bKH1a}}>F'oLIm)TJ|Mxt8veX ]L (mRm)aVVB[EI)1U' o+Z9]$sy>u)ڄg3 (S-&.f2xEs9MNgMQAɦ%{klvDL5v/DuAvi7^ aCƠhJLEĉZR_Li^`RBj3.Uf_ag9/%1f_D3W2ߗsu )JdEIE"T Lg;c<*1t?/g0s6697%NRngxmq,e%@~`> #/uw*F"`n믿onooIZo72[$S$u6p rdX61e^Z)lji w\HJk_IM6o_׎X\Zh\[woj@'A 3Rŭ*Y^級g6 (b*G޺ddZh +)Tv]( -TA\!EHYyِyUHWIry_~ q&nDӼvӧO>>ɲowts=9sc VFCrhZTz0JU0&GhjL@QtlHڢ#ԜX.-7#Ηbh0FwӄWB6%K|V $٘r.*ޒ[tA~Ur@6HHTZO'Zqdh1'A wvslz|!Cͦ}uwsOSK^8Gm+b,K{6 j{(뎲?}oG;YHegBBrӓ~>ל." ;䕒>ІRKTA#MV^vފQ5a$H%MU!Jr2}2jWiaWR)&"H#nZt {rBs' &lhj rSq R^N1SpI;+dlESX,*9teَ0sny5zv,Rw,f(]ljTItq!q)yɶN B}<>ǿw 0/v]KiAGL8˳Aҍ푘CmvSK17* h"N_ ia1ج {=Ivذm*aٴ*h kG4S@vQ굳g-DP0a};}FmKˆV)VwZ=hDkZsnl$Yfmi r Pf9 _- ,c\G2C4,b')I MGP??Ya"wEE{\*%f_T&.:))xʼnyž2I I|k$K7-h0*H lK@&w-@gIoAۚ~@#Ba &ʯ+CK يҼbg.2)}&X$S"S=Sr8Ec ЪdI:vIϫcTlNI9.].qæ 2Io44G?-MV"Bee7 lDi'YETBͿ (|Dw'V}Pc; s{P:Mduc׹o(+B"-&?wGDoR˼ъPB@:+/s?3q4!;׃Ywʅ4,^9AG-l>8DKѡ-O:斮OYI-1°W)5q4q[onnnx< x5Zvs{RYC;7aﴯ}c`yҳ,ҜOgZ&wA]))ClKY%9ʎdt91U)t]V b+LVuova#m4\*R][0'䒌p5 HDwm+b0C tvU|ƞz6-0.jar#zl$e d W)Z*{| v$4\"^Hl1 Vy%f^FC&v ۷_94icVx2p4)'$u(&&Am3 /9}[wXg@RvFz~ȩ3&3_Pvoۛ-ldFШZ_!M‡eNQԓ~H#Uuڴ\4!;;Z 4tҰ7yvQCs֜%J6X}BpE07TJ.Gܲ)/#›3f{-[ѐ!X6 N91Yu3bVUi!R@0a l(ta;/`'R׆]Jv,F}""*Sbv$m#&B#}m]Z"BoI$ϰtMl;v}/N k, sNxh[Յb>{%;ːSbf,W y 4XˍԫXKsLvT E1Bju{Ë~z,z^Txb\Xjx q̝1ΏpCz-`HTmܽ~';U;}Qov4&1IMDĎuuh7Վ%sUmHM^MqN|t¶2АC39珄Cr_`(ĔbH!%D25/+NBQ75A^uc/i ց]sE3rEVXl]x_0&D 2XCqjh4LVb{[NnH8N'on͛v׈Yiddus@<Tvp WkH9 -藙!= Bܒơ©f\ V UwoG2Fbcx%hnRLNj$FqG|nJr 8*o2<"&T4M7H$\βՔEH!$G>`>F"\tYX eƦh, +np>ڄpW&l ̓Go,me5̄&*?u%:ƑQGmsZX>'<^ fgjkmk?6?I&htnNa!1l;uw_PZ3].WuWfu3f2jJOڪz|#Uv6l9\''W$')\9C?i^aIV͋y+T1Կ-sP Hh>4XN'N| 0t~zv%# dבmnjb#0)k3'i.Bw s{գk8"ЙP8%#S.nOy߶vOo-MƧOah:l+: לL8 +qp{=1m?ϒc\Ar5@MMxu@ MoOyljS:}?8YURPOT&fۺ+x# S@h# dr vsT@5N#[hjgAyA#z;LC.[( f qGĝӌFHʸXLun}Eg]i &?NCnW|nO<ۇfl7 =8ϧax:iFS"v?l~jvup1Db8S ?P}=}S7w2C}vXD]7,͢׆U*Ugy ߁ǼoAтX9K[d~|qND3q$}^ 3Lb >b!,i-8HX zJƆFGM{[U+}nOTHݮ}%?o?g|;L.붛}Z7ϗ4N#=їy/8%Yr#F7Amsa~ݜ=کG TBMի'2<{׼#O!ǻ`}EbDDȞR9$lxWMɗlL >FӍ2)2a-{L3b Imxm"`EuFnI#ι;a$[@ޢ.(ҋ v˾K59NJg!Ӯr"C|@v:;ϱ'fG݁?eC~oK۞lbv78}$9OeElt?n7{7/no-:i̾9\C[Z|P뾸 ?ߴz 4mu!:S[Ҧ E,AB;L_bSiGa!CHJ zMYNiI+H%R k%杍%h7%=M%.%cd Rݫׯ޼%aODKǁo"JF3෻ %n/h*ֻDsa?iLwɦ:t7Ud`8ϗi8tUĎ+K|iZvۡRԆ3 AͫfʒaVnԞN z@-M0IR˹+\xSJSVpڪR"K0œ7Y}z1#لfiQph(: vsʋP!PW? ]eY҃lV`4®ݾz}.8߾~II×R<߼ko|f;t!ҽ/stjQ`/n\"TGW)`mNљ$6Gbh"mڻ-Uo lc:~u<=-zﱯ5?~<;QSe; ˎ V]=TzOWUWWHf&Yi\tJOIlI!Ƭn),ISUT$"57V@RA"AΆX}#z.7 T܎u7=69(nG%9-/_oBIm=ȤϏqpZI{FAv#2֖97O$^ö^.T1PyWcw]Ohra>CYP,9iXK%IlPǔSֱbY+>î! .'>8,ȡA!Wnj6K BG n)㴚+ RygMW$ \&#)G<ݶ:(x|B HoAzw?4Cr-(B.N#@=p~KClWE#qv''/ۏ {>!4qN 18qbl~S;%A0RʣD{{S?ө_FK:qtHs p"#m,*-y$*!]Jw^+ )!Yj;UW,kn_ itG-0엔ώJӁ3JKyw x2!ϼ.z-lQ6'ꈜ wMAE8HoHmm&d;]O0Avl:5v?3UvoRe u =/\䝭k)*aM(a@p)Ų־&VԛJŊ/}c8>? /n>=S0|a񸭑M?g1u@KzI$>>|DeO#q]#ouL4HNI^zg#귋M\!&#{&!N皣kq1!~8ut1(QZ4Hj|~|>Įih?iKs#/KZ/VXl\gL}a8i ?y&f bvgx_,e6 xja=JKn[xWVSRP&1 [a_.'Og\F A .c|"tdg= <7dÎW ŴƁ5K@GYbުg'Qeفw&UU-E@`s)׮noCqf3Q)룆w*0J@ +%f`͜]e[ ŵGXꯀǙDc'Yű}S~a ex9I Cshx|<=.EwdtH]CjCj>d)gұ敂 ('8Kyw;Tt#cpBk*ډx剔WWUnYtJ9p0X0H9 eM5d/5 fFس\GT D|5"*1%fkń 0m@WqʀV17g'7Y=h5ɜH~ݒ9\~ī 66+=Zb e[Mc.IHF*5CDŽx*{`Y5o"1%woMGZC mD&;I]ܫQw^`!-uWn`h#+dA<ޓKږ~a⎗E~Vg`7"gY6B9̸Zwj%u37uܶ$c!t$25)GF&*/CXʉADyt@y4E&b `LN:HߘGJf {npqCO2݅e͕ Zhp~VؑmíW-V4dab TSRc󩫶4{dn-*ӹ7벴>,]<7P:>=?=H ,aUP2}ɕ巚A! 1nCڶo)9%EP7zy V?a!]GR.d*f_AAs*T0Vk^hIЂ̶g|T vS.rn<wpD$~®(h@;:P'06x ugXi]IDW"~6qDRo~Pd[ @?Ѕ_pEM`77 ?_.ϗ82#v/?K>A)Wv" "ڨ10ܶ ӲIy>cB9NdDr4mv *-9\petLȥ++44<^tI;+>4~qϮJ}YF6G~wJGupp~苦Mefn#eW; ktK0ad^ր8Hh??}Mn]67=4;ېi#q^pQ\33VT#?.[&fV d 㤰o}]utCҍe kWYiݰ IDMM!|zYW y lfsI Zb[tҍRK ;. 6\.suQ)cЉ<%Ďmzׯ{3gڇhjHnBW~AE6dQd]8KA$$'p/Kd_þow(hbd w_9yĝ))~!gULVS)։E3:T*4 hOsEx^ {M%-#HÜ{~ ﵤx )4iz{ɭbR K.q{x{l kM?0G 'i_yKH FCalCrxIG$3mi5.x"iq[V*}]W8wo8=SO "1YCjvTeYmD`}[t}lۊdLƇϴ|m660!T+nbpKlEP$;)w%z>9C晎vNJH k%8άla6j5o]ix^  {~힟w݆>'lyyUU6EsLO^%=XYm$i7=a\Г(WSW| .]yrKH3Ho+oM"`Ni0?Mm3=("߁THlדDuY.JK7KVȣftŕlΕwXeɉD%,~r:pU|^}fV{z5qz3ɤ5 M/\8ߒ?gM "!te S?J)E> gf?>tMgqܩ_|syAL |ڇß?^ti9< "̷Gx,7߳VGK Yn Dpڴȿ:4_g{mOKYIde*+"vxm_k5`șREa:X=-aXL5j b4_!61',$.SlND4S./WI1PR\I9Oe4/󹠪p?Гv-riG9FI^ w|/|S&< RLk=`C#G=a ZSUϿôZ ._1wSq~ǩ;`g{5,iC\׵ɯO//?1}>1_V%z1yya&D|X:yPeoYg-ͺʨY {k0΄:W6&{ý)\Q*dlͅ=^(O|=5eqBhgD˥,:Zl]?$nh|v@R:e{-sB=X )z EȄ=bn_8t W!(%_y@5O%=\ffd "I>$ ퟟgItv]n&ݩL1eIpVr%n̍dY،gsi_6{f* s0 "bk`[&wE>.֓9nkJgeշ lHډ/_=ǧ!7[\;MJnS"O1*t<3?[A,_qYbj/n vN0֗[uIJU+k= r>r_ʝ4iu{Hr1Q܋H}'-xߧ&]PY#-[]-x@SgxqObdtρ؜v **ѫ<~y^f>. \uџruLjpT dZNF>>RTU`%^o 0bbEc<PK@[֏)Hwo+E}_<4',g _Q<%v]>nXkXVؿr4?_ϒ>P[GfiockeŽYaX:̒CPs$!|jXaӫ*Юvsz OֽJk}5OԈl?S^~FN!a-'@e,.Ga C}y$uK*F\m[G??s Hyxt TA r} ß㋘&D$(^? _xz>-s:x$LK`lh؈yT [g|;b dݝ9;7׿qt? `=_l lJ~nJQ#)L+5۞Bऺ:xB>HqItɓW| /6ݡ"|P8rzzk^Z*.p q9.EB _ұp fh')Das7>w4EPur!2pPk{$FS! QaZg\WP}W~*ќPr1y!L֫\;j.?D,O>5 ٕUkC۱aɘo JۿVQy<>49ФjoxVZt;v3}WmSJ<0ׅ#轔%7`3fF|)ܭ^nq~O~Dهdab$_Eic& ; 1Xk}l/cǝ8q`<`2(p<|k4T{.KOx_9}wn Ѩ=H*J|wfصfTa-;Z]I=QL)/s3bx1SjI}h`|Ow->ކqJ@QtfIUZJ{Ū|AG#JDR.| T*JHQ9ʦlFyqL9"_<.a1[>34Rfhr9|=iCw 6d;V?bcMjIUnhK6w]N}^Ap@_4$%,-!5\ u:lKN_4>]kO]FL^4 ݈MwO/;9)ڽŵ4@u2݀^;ll&H8URD ACP2e#U1Nr#0b] 9C?1B56 < J1>JXG[(B0kdd" x=8=V%~4|ܧIRa^r]6_r`̽,LjE-N'YRעyURX?u?-7˝װuoW"?G Z:0̀T=ΖV-bhs2uaM1={7#[ )ay4ŀ0厱:pLݼpUJZmIrχ9*G 1( llˢpbH^=/Tn|}0AWt<qX!ӓRR,1@=a[s9GIDP#X%F j<{F|bvǃ]Ȋj('GA\}:sH.uG׽.S9r{rJe]{>c-7*q |<\2u7ymozsdBդLP < ׎- piAm Egb7fBs0Af XfDQ0m-ngi<6؏^n9i^< aĉ}dzَmhډ!0W/I49o~~h# [&\6|r}# KNqOL[tĠ3Yda cAҷU,cKN T |i֛ ޏ;o4;c8k,ۺ~ RO ӮĀ** j^+p3g7lI#0r:d3u^;&Bj*$`FÄaqP[z( zGG^ľ6!4b6qsQU ~J[:(g޵qL;p:3:GKkfxLJdԒ.":)yPjIA0%Z%?F䎗2a ~RNAՇҶ=_ $qx HH{ qw3d}P.#m栒ES9u ' h4hS5|G^KsGojSZXvC0pb^x'0C'>fƾz笣?Wv#rv{%.UtC'J S|T$jbRa\< Ρے PuM㖧 f}>c!(`xxwf1(gFF&my$IbPoE[8m?(G~C9[N߸YOJD[wNjtJ;&,fX$5ki :F 7:֕;1}p㒹e;&!SCgj'q]8X<͘hCFWYJ}CU5ڴJ KF훀c [\G TSE;Q@KYYn4&c <룼>-n1S vjY $FLUgcw-<( DZ)ԸY<<$؍X 3%_`oo=UW1d3}sD IM&Frmɯ,unYo;QWPQ@H]SB&"eL(l):(.ZIk7r/yKvX ne4 Yŀ(KÀns偢JeYPtX] |'= (QޙzZLbϾW;^f.EM=ˊv빶ڤrz-b@G^ZO=mcLCB\J\C/8Z~XOnoo⇏?iyqm,"Qb%grUC[V;/իt~/ GTP#t$s'A$O+'&Héq=)Kʜ O-s- F𵜖̊B*9h| Čojt3эŬt=r.(et7piOGcgl{&n1 [:=gEy-< -ǃPGq!C?O톙yCΉz8cc@jۦU00ćv]] k<r*5" U`h2Hod}glJ_A5&N0D r4}Iĭ sr<ڞ8A =˥}.ʂ`ypʴ|HFmWUJaaXo/ d#r ڰmCkklAo6c3d"u62T[V7wK<۫vz%gX9жwX]y2GRyx6#U‰\E=;]>u]ǽV-Vw|*c՝o>*re~2B~, G=^a%Md}D} H-  0Qn'50dXhY?f1*v([i8,Ofy)L0xjO:*pEP|1ցȁ+ʰ*Ώ{00 x~~ aPŪ`KB ;(8Cg~LI=,$ ] T;1%`U sl6i X$6sv~mӪf񜵐?U\cz -FwrxŊhv$ 5G/FG5AAfԶ} F^zE<.} OOO_6I64#1+`f'X@i<_HRu,_56FiKe~ ?t:W;34 KplK&U+92| zOM>B9Q%)ˏ*~1e*w:e{̚5G jJ[ ` x"]-ϋ\@6&hFSf bXo:A$g+æ/ϯȞ>؋JPvv֎Q) 0Q ˗/0??2YBt6I^^GNG0L?$QNA'd`ҍcv1bQ5]2Mt0s{B8}=G5IDAT qf6ndKmStbS֯>(R7 #(]lZEcW%ʓr(g9h@Uq\lYM /u,Y!5yn2 m͠xݣ-pF&F-10U+&Ӌp+r2] '/#B8TZ9g lh|>\y 5mlM8ZSt%8c:QN_XB+4/?z߻:k]pGؚ':(0zlBUY$0 EVJWo5pՈ]Qٔ͊F2 .- 6J^M\mXBؐNa59osk(JYB '~ЮM68Fhy%Fd-o/[ҐZ55"]v:*Ts/USNkNX<[j^|'!aQV+cKJ2R;`"͊TzY,E HL` f&'6ϤS*\۴b.3Hu#y~$SQf;6@Ѳl쒷]vaPa[Ag h!G l:<0(q6(PUے@Z.\S>r`|KH`o/To?r63 ZЯń|biq;۴E*؈&6yRIoIdWȚjۘ[7 Ėe۟@g+C^Géx>AFĚoY4-(hG1#'=X, 'VS_UȲSjO gС?̮V< B/f*$$ L>*JFX&^L֑/u?PԶۀP҅ngQ7{զ֖z?Ce(VS ;DTm2h&Vi.=U~c;&ԝӴg̒GBɹ O4$ut~>vNY/;ݛ5L V'tUlhNA.MrZ#8,vMH/x-ɒt>=?^~[kьtzNZ3\K@C/Վʑa3i&!4[s#$7$j=6ׁ^Fs#i*VbSXۧh9mkK73pOf 8h% =%rXPZ&RMֺ֫7ٲiZa\$Vqc&tz##O=qW"5E;8HrY Y 4LR*#9=F{0I_ۑscUAQX_)S~zA 9⌜5iVw,9f~}Aafq]Yq*^x;'6s1u󛶹G-el7ԥ|3Ӟm ʵHavEўɎhѭBo HR wYyI ͊]l(XpLaZYǏ߿MTwݒx*[9FjgdEv v#Ԃ* ݮzs?v>+!F~jmi⋛km5.3*i-v_VڷK {iQ3ւx ͋$a˜>t.H ?m \"7wAYL;_{1M`G;&Տ-KOܮO<2VKaаϰL-=ɬ z?1ݮK3 d܈rwh/nUz&.]YS" l]3-lv3PoSh`]jX0KnF!Z65TaZ Ws-QOE*u4?t:t%- q[PؓHdN۪_4v@PyZG-mbS *&9KmzV}i@LXfH"XҼ#Q7  .B!URz5!c/!ME䛼4. D`XR db瑚;Ql>ܘ n4 65cĭ:yM h<|RWhQ""ޛ bO,,H@v\>>X}R߁t+i[L뇑g2/kldZlآnek{যl>MtjhDup`G3{ɷԠDTjP:<%*{n?$<]~j8*1vy#S rz:=2ĭ]/@KC$W'Ar7 !hV~FOڊ`)d&m-5z]2uYQ^bdWWR'G+|@N~?>%MEMҺrQ:LI+q0gN⫯8?-<َ2֔։vS/t=jwMM 4^Dէt{u*#Y:OL*7B52b߿ݯWt nUX Hnx\/o߾=Xu=c&|ځ#E 7f E鱍qF^sc_\ҊzCYwC0=M&՞k"0Д)A[+U>?/-ʘCΠ U/=^@ndt@܇77 y@3l\!C`Za|h`6^pUi>8JZq] U^W*A\/Gʍi<霃n*$8tݮ߾S|B7E<<1 b0wyhۊVv꙽XLH!ɲl9;vJϧIS=7q}dR4WmtBrq!?ȝɺǃ0Q;x_/ 9-kW^Q؂x/&rG|oZg3Hw"sy~Kf kf =UѤCgw e#|kk`:X@D%l~.m` >3e&54jy3)D؃i?%n*tvh4Ie{fJj6h.ۀ3R_J}{{?q^p?hOxԃ<:a=pn]sV`&M X~a]xVܒt3n,D~ ?mBt wTKׯ_^~kLi?~([)L7i,ΣO4M1`MD+rFQ]%_yKzR-1T%x4pg%I!HsXL1MTh,\^N>ζT;bS#G#`]t ayI~dU9׻1L=b*xJ(EtfAUUAzd,+HKѧ{!mq_~yOW%I|q׊CPm3wK}TPUk)8*QZ㦓!&K4xcYDYmsU)5j1C^vnBa zJvpר.6 ,F6Xb#lz7$J#$mp] ʃh>On׆ ey] !8tU|[^a]?G #Rq]RXȳPU@U HzlGM)4mZ,5|E*/9W'j?b*>0V ,}բEhg,rSs3"X@QLmKLrKW- bPbqffZݚ9-]Rw$FTI~Qc ;}X%HL"F|}co2RzN{Ј3gIDǞ.o+A:Zk;լ+$8?PaW>Qܻ{U, aWRQ]ͯ^-&oj}%R6qh 59X+ <*;8ؤAxZ[ΐ:ND ^T-j)qǥ3nB`,eIRHo-f(bPQF}C9N1lEЄ7:$Yc -=o͡jǚaIsDVS0hy֡n=e#2u7S^#r[dHr=aJbsQ -p!-IhkٽJ #3 ;uMBCk7Ɨ罿DMғ3bgFo\.Z3?`ΤS.:O\ψFk  +I,zdN,ȣe>Jhp}¥?l8t>v~x>TqhbkMWtX`=hy5ԝXb0U յQՖh^]|PՓ;WFU>f:fMx*KSNlޟuC%8tA6_,' DѱW>ߑD>YCu4!Ġy6S 3!:JӔۏ p{~yP)DtNbLǼ(ԅ^^^j$j GHnp¢.bs *pa

,<%h-jrΝ9)ke|Ƅ@,`TaUQktVͰ*Ɍh-J:`¹0YOi@flt4l)xFe;^ʼࣁK"k[. u@]1mYdh#uL49Ǵ+W]2[ J⡯Rc#䥦;eya]  8O0o[lFtÍ.=VOח oLP bOtTx&덬䤔1rSp'caCk)8FUHWX$3 ÁQ%N"eSd+N1Z5|["=8uPi9ї? i pMAo 5E>ō*{hLA_;q@ #CǣN2ǾkO IA#f(; ɥN|MpSG]HedBF;`ܷwQRu.GskYJo!!jrKViudVߘ@FϠ-#5(A5eq=PrX;0k++moK`jSd]A90z>C?{O5mPRl&Wv {.3#gq(}w-[%h:h N!鮹a\/UG 8$^.02UIY[lD6 cF&eSg$E*%s!P`nδ9VZڪUThZ4sy [in)n(t,9a !9wb'+`"Jw% *ِ:?:1oc RGH%gWZP@KJTV<4XjN7{v찄Ozۨى[Ўl̮77O^M<+dnLϠlW ^3Aϊ>) [MOHOmM -j~ B1 Ò{7rVOqAn]@W' ϥ\NK,@mPu8X.$𥋵o@Adb@T,";H*ww.-Ovbz=6ʜ\Z~+FL*ybeξ!MZoL{*IIj$OK+6MZ!M3p&ݖ v˭URvbsX%ڬ]Ġ0AG{n(*/#I zwSjg\U&%Ƭ`55Q֞XqA  Z 姁3.?C. Sf^mPЖez>a5:El{n8NNk [m!Jt6)ӸNSUP6NVl̻V#5̪DRl/7=CDueVaMEٷ(4@ S}Iz Trn ڦA ̫&sS:yd0-pBPAmJY?ʵ 6ΡФJG{p zoN soYmF9!ϖEJjjbvEИD ! ϋI_jϴCOqf0(M;!x_epj'Z'rޱvg`Um*\eWM>V뢪VK(EU@&۳нZL0XnNzm/t액=?ldVj{) ;#=m36`qsSEA}g }{sZجGo5}-h* O9Cq3VtƼqpg#P5c kv$VC!l$$qWDYD]),TU͞W66j,+dy-H͢ }gJ.5l#%[)Jhtn[Ֆ:E5{lޝC(ѫvðv6NOm{5"y>z2Z"Fgv賫bKe7a;Ը!ӧK[7H \zn\SZ, X zv#({-[ta0ʥ9GG37Y0嚕flQEtɥ!Ufd ѡor T0xfd/0f~@M N;}9E3[0Ҙ{C1>yo7 Sr%cU'P-c߳q^r;p#yxo(,h!pR·U[@Lz̹nw̰,&ظuB]Z^reFFNq8 Eaf.4U &e"rk+ZC6FU]  Q:A- Vod+иZs]fZLBIr2U|wCq$k"m/}PjLU-16MS@WCVval8Xsi]*fq({ g~%o'|p>?NqJ4ПVMݠ6kƵz4WY6GA؅aYsG\ * V&+Iƈ] V[WB J!1]`B;e` xcPZϧ8+j+YW@WK*K[թZjF9cpT2Љ>(w +(c$2 rXWEQ Qb|2Sq%<9HS4 Pc=o4`l2` ُwLBgmaWpglÌ@fب6mLuU`DgDp3CXW=i9ME>`W'SOO/qHYע*G jpm*jBm 4>$yj":q#5tA-/h*AШ'4BcۡUk 9xz@ZL1&MBs3[H)PU S4dLmUŃ: ̊&r[k4G⥠B-6[ 22*]V?Y+1CHu~O Iײe@.$rcjSWnޚroͫzs~BExchhs-E& m ,*Rr,qjM#@dݺ"g4 N \Qpc Mm1 ="XfYl0Yq< BiLF$J hrqz^h+~ɍ V2b_Ԥ&8g pMK%ŝWOO_; v (x܌SaO5O@=K3(MŃEvI usRlwo?F;l1FZ=(m&TT6DTm@Q+ eZ6beikSW.{֡;eKQv(z)1֎qH伃1jk"!N1oD2>?uČZj{qg!]r XHOa&ư[yz6t~ʼn|vd,,a q4sZmvEdEB X9p,DsJ&*ꐀ'ӧ` #7ƜϟX;njQ7qtK)(pq*Y[tK޾xoFz6PZ٥!5x"_k`+'OŴTTt&l1=BJ=kܠ%l[ $aG1GԧCz}R%5fUBQkG"}$r@<-\[N{c n,kXw!pӨ| 7(T/cƚ vb'/M,!4kTq omz&7z%ֺ7KϽ@F輀`ؽPRO%:Y̴[LDŽh}0$nw%ǒ! ?dZ:Q`.#1l35u!l@[A1mԻ[ק؟vmɨC-:᭪ҏѩ=ڒ ]SCGu8JmD֗+ZVYvZxU`/PEl8XhlĢ"Kق6,RF-, Ni]Uᤗˁ8oRSXQ/ꆤ ]8??;Cu-dPGe= >{Fm6|= /zY5xBϢ?y=Oٝ&WH(6 ^ W67Uh,?^5hCE\jcoRWB}SJOb.z&s_}90~$ %=v[]gլC.i8 g1kv%N7NJIX-iMkELqƆOӢJvvpo3!*.ڬCp|u"lP l^ƒ$ʶ= 0K9˂[S-eQ} Ih%?d s^¯WV'dBՏrJ`†-b|vQtQO&EwCTs}'oLa*]] #MEY^ &0]6v_UFQi}Rk1sT a{g7_Au&82jܵ ƶY2?xk>Ə,r%[$.w7\Od\hOA^C;ZbZv9a@Cƺd1XR[M'Ƹُ_apVD1i ;vgZ[nҾssxv ۟Hcr2 ]Oy3I9l9qq\\[8z_g{kЏՊh{z6t]nn1 {wu7ot|CA^wIENDB`process_viewer-0.5.9/assets/fr.guillaume_gomez.ProcessViewer.desktop000064400000000000000000000007371046102023000241740ustar 00000000000000[Desktop Entry] Type=Application Version=1.0 Name=Process Viewer Name[en]=Process Viewer GenericName=Process Viewer GenericName[en]=Process Viewer GenericName[de]=Prozessbetrachter Comment=View current status of processes and system Comment[en]=View current status of processes and system Comment[de]=Betrachten des Prozess- und Systemstatus Icon=fr.guillaume_gomez.ProcessViewer Exec=process_viewer Terminal=false StartupNotify=false Categories=System; Keywords=system;process; process_viewer-0.5.9/assets/fr.guillaume_gomez.ProcessViewer.metainfo.xml000064400000000000000000000017311046102023000251170ustar 00000000000000 fr.guillaume_gomez.ProcessViewer MIT MIT Process Viewer

Process viewer GUI written in Rust

Process Viewer provides current status of your processes (cpu and memory usage) and your system (usage of every core and of your RAM, and the temperature of your components if this information is available).

Guillaume Gomez https://github.com/GuillaumeGomez/process-viewer/ https://github.com/GuillaumeGomez/process-viewer/issues process-viewer.desktop process_viewer-0.5.9/assets/fr.guillaume_gomez.ProcessViewer.svg000064400000000000000000000113321046102023000233130ustar 00000000000000 image/svg+xml process_viewer-0.5.9/flatpak/fr.guillaume_gomez.ProcessViewer.json000064400000000000000000000023541046102023000236110ustar 00000000000000{ "app-id": "fr.guillaume_gomez.ProcessViewer", "runtime": "org.freedesktop.Platform", "runtime-version": "19.08", "sdk": "org.freedesktop.Sdk", "sdk-extensions": ["org.freedesktop.Sdk.Extension.rust-stable"], "command": "process_viewer", "finish-args": [ "--share=ipc", "--socket=fallback-x11", "--socket=wayland", "--device=all", "--share=network" ], "build-options": { "append-path": "/usr/lib/sdk/rust-stable/bin", "env": { "CARGO_HOME": "/run/build/process_viewer/cargo" } }, "modules": [ { "name": "process_viewer", "buildsystem": "simple", "build-commands": [ "cargo --offline fetch --manifest-path Cargo.toml --verbose", "cargo --offline build --release --verbose", "install -Dm755 ./target/release/process_viewer -t /app/bin/", "install -Dm644 ./assets/${FLATPAK_ID}.metainfo.xml -t /app/share/metainfo/", "install -Dm644 ./assets/${FLATPAK_ID}.desktop -t /app/share/applications/", "install -Dm644 ./assets/${FLATPAK_ID}.svg -t /app/share/icons/hicolor/scalable/apps/" ], "sources": [ { "type": "dir", "path": "../" }, "generated-sources.json" ] } ] } process_viewer-0.5.9/flatpak/generated-sources.json000064400000000000000000001100501046102023000206270ustar 00000000000000[ { "type": "file", "url": "https://static.crates.io/crates/atk/atk-0.8.0.crate", "sha256": "444daefa55f229af145ea58d77efd23725024ee1f6f3102743709aa6b18c663e", "dest": "cargo/vendor", "dest-filename": "atk-0.8.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22444daefa55f229af145ea58d77efd23725024ee1f6f3102743709aa6b18c663e%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/atk-0.8.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/atk-sys/atk-sys-0.9.1.crate", "sha256": "e552c1776737a4c80110d06b36d099f47c727335f9aaa5d942a72b6863a8ec6f", "dest": "cargo/vendor", "dest-filename": "atk-sys-0.9.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22e552c1776737a4c80110d06b36d099f47c727335f9aaa5d942a72b6863a8ec6f%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/atk-sys-0.9.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/autocfg/autocfg-1.0.0.crate", "sha256": "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d", "dest": "cargo/vendor", "dest-filename": "autocfg-1.0.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/autocfg-1.0.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/bitflags/bitflags-1.2.1.crate", "sha256": "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693", "dest": "cargo/vendor", "dest-filename": "bitflags-1.2.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/bitflags-1.2.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/cairo-rs/cairo-rs-0.8.1.crate", "sha256": "157049ba9618aa3a61c39d5d785102c04d3b1f40632a706c621a9aedc21e6084", "dest": "cargo/vendor", "dest-filename": "cairo-rs-0.8.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22157049ba9618aa3a61c39d5d785102c04d3b1f40632a706c621a9aedc21e6084%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/cairo-rs-0.8.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/cairo-sys-rs/cairo-sys-rs-0.9.2.crate", "sha256": "ff65ba02cac715be836f63429ab00a767d48336efc5497c5637afb53b4f14d63", "dest": "cargo/vendor", "dest-filename": "cairo-sys-rs-0.9.2.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22ff65ba02cac715be836f63429ab00a767d48336efc5497c5637afb53b4f14d63%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/cairo-sys-rs-0.9.2", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/cc/cc-1.0.50.crate", "sha256": "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd", "dest": "cargo/vendor", "dest-filename": "cc-1.0.50.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2295e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/cc-1.0.50", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/cfg-if/cfg-if-0.1.10.crate", "sha256": "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822", "dest": "cargo/vendor", "dest-filename": "cfg-if-0.1.10.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%224785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/cfg-if-0.1.10", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/crossbeam-deque/crossbeam-deque-0.7.3.crate", "sha256": "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285", "dest": "cargo/vendor", "dest-filename": "crossbeam-deque-0.7.3.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%229f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/crossbeam-deque-0.7.3", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/crossbeam-epoch/crossbeam-epoch-0.8.2.crate", "sha256": "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace", "dest": "cargo/vendor", "dest-filename": "crossbeam-epoch-0.8.2.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/crossbeam-epoch-0.8.2", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/crossbeam-queue/crossbeam-queue-0.2.1.crate", "sha256": "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db", "dest": "cargo/vendor", "dest-filename": "crossbeam-queue-0.2.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/crossbeam-queue-0.2.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/crossbeam-utils/crossbeam-utils-0.7.2.crate", "sha256": "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8", "dest": "cargo/vendor", "dest-filename": "crossbeam-utils-0.7.2.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/crossbeam-utils-0.7.2", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/doc-comment/doc-comment-0.3.2.crate", "sha256": "807e5847c39ad6a11eac66de492ed1406f76a260eb8656e8740cad9eabc69c27", "dest": "cargo/vendor", "dest-filename": "doc-comment-0.3.2.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22807e5847c39ad6a11eac66de492ed1406f76a260eb8656e8740cad9eabc69c27%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/doc-comment-0.3.2", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/either/either-1.5.3.crate", "sha256": "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3", "dest": "cargo/vendor", "dest-filename": "either-1.5.3.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/either-1.5.3", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/futures-channel/futures-channel-0.3.4.crate", "sha256": "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8", "dest": "cargo/vendor", "dest-filename": "futures-channel-0.3.4.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/futures-channel-0.3.4", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/futures-core/futures-core-0.3.4.crate", "sha256": "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a", "dest": "cargo/vendor", "dest-filename": "futures-core-0.3.4.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/futures-core-0.3.4", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/futures-executor/futures-executor-0.3.4.crate", "sha256": "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba", "dest": "cargo/vendor", "dest-filename": "futures-executor-0.3.4.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/futures-executor-0.3.4", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/futures-io/futures-io-0.3.4.crate", "sha256": "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6", "dest": "cargo/vendor", "dest-filename": "futures-io-0.3.4.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/futures-io-0.3.4", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/futures-macro/futures-macro-0.3.4.crate", "sha256": "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7", "dest": "cargo/vendor", "dest-filename": "futures-macro-0.3.4.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%229a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/futures-macro-0.3.4", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/futures-task/futures-task-0.3.4.crate", "sha256": "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27", "dest": "cargo/vendor", "dest-filename": "futures-task-0.3.4.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%227b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/futures-task-0.3.4", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/futures-util/futures-util-0.3.4.crate", "sha256": "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5", "dest": "cargo/vendor", "dest-filename": "futures-util-0.3.4.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2222766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/futures-util-0.3.4", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/gdk/gdk-0.12.1.crate", "sha256": "fbe5e8772fc0865c52460cdd7a59d7d47700f44d9809d1dd00eecceb769a7589", "dest": "cargo/vendor", "dest-filename": "gdk-0.12.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22fbe5e8772fc0865c52460cdd7a59d7d47700f44d9809d1dd00eecceb769a7589%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/gdk-0.12.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/gdk-pixbuf/gdk-pixbuf-0.8.0.crate", "sha256": "e248220c46b329b097d4b158d2717f8c688f16dd76d0399ace82b3e98062bdd7", "dest": "cargo/vendor", "dest-filename": "gdk-pixbuf-0.8.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22e248220c46b329b097d4b158d2717f8c688f16dd76d0399ace82b3e98062bdd7%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/gdk-pixbuf-0.8.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/gdk-pixbuf-sys/gdk-pixbuf-sys-0.9.1.crate", "sha256": "d8991b060a9e9161bafd09bf4a202e6fd404f5b4dd1a08d53a1e84256fb34ab0", "dest": "cargo/vendor", "dest-filename": "gdk-pixbuf-sys-0.9.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22d8991b060a9e9161bafd09bf4a202e6fd404f5b4dd1a08d53a1e84256fb34ab0%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/gdk-pixbuf-sys-0.9.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/gdk-sys/gdk-sys-0.9.1.crate", "sha256": "6adf679e91d1bff0c06860287f80403e7db54c2d2424dce0a470023b56c88fbb", "dest": "cargo/vendor", "dest-filename": "gdk-sys-0.9.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%226adf679e91d1bff0c06860287f80403e7db54c2d2424dce0a470023b56c88fbb%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/gdk-sys-0.9.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/gio/gio-0.8.1.crate", "sha256": "0cd10f9415cce39b53f8024bf39a21f84f8157afa52da53837b102e585a296a5", "dest": "cargo/vendor", "dest-filename": "gio-0.8.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%220cd10f9415cce39b53f8024bf39a21f84f8157afa52da53837b102e585a296a5%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/gio-0.8.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/gio-sys/gio-sys-0.9.1.crate", "sha256": "4fad225242b9eae7ec8a063bb86974aca56885014672375e5775dc0ea3533911", "dest": "cargo/vendor", "dest-filename": "gio-sys-0.9.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%224fad225242b9eae7ec8a063bb86974aca56885014672375e5775dc0ea3533911%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/gio-sys-0.9.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/glib/glib-0.9.3.crate", "sha256": "40fb573a09841b6386ddf15fd4bc6655b4f5b106ca962f57ecaecde32a0061c0", "dest": "cargo/vendor", "dest-filename": "glib-0.9.3.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2240fb573a09841b6386ddf15fd4bc6655b4f5b106ca962f57ecaecde32a0061c0%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/glib-0.9.3", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/glib-sys/glib-sys-0.9.1.crate", "sha256": "95856f3802f446c05feffa5e24859fe6a183a7cb849c8449afc35c86b1e316e2", "dest": "cargo/vendor", "dest-filename": "glib-sys-0.9.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2295856f3802f446c05feffa5e24859fe6a183a7cb849c8449afc35c86b1e316e2%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/glib-sys-0.9.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/gobject-sys/gobject-sys-0.9.1.crate", "sha256": "31d1a804f62034eccf370006ccaef3708a71c31d561fee88564abe71177553d9", "dest": "cargo/vendor", "dest-filename": "gobject-sys-0.9.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2231d1a804f62034eccf370006ccaef3708a71c31d561fee88564abe71177553d9%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/gobject-sys-0.9.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/gtk/gtk-0.8.1.crate", "sha256": "87e1e8d70290239c668594002d1b174fcc7d7ef5d26670ee141490ede8facf8f", "dest": "cargo/vendor", "dest-filename": "gtk-0.8.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2287e1e8d70290239c668594002d1b174fcc7d7ef5d26670ee141490ede8facf8f%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/gtk-0.8.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/gtk-sys/gtk-sys-0.9.2.crate", "sha256": "53def660c7b48b00b510c81ef2d2fbd3c570f1527081d8d7947f471513e1a4c1", "dest": "cargo/vendor", "dest-filename": "gtk-sys-0.9.2.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2253def660c7b48b00b510c81ef2d2fbd3c570f1527081d8d7947f471513e1a4c1%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/gtk-sys-0.9.2", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/hermit-abi/hermit-abi-0.1.8.crate", "sha256": "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8", "dest": "cargo/vendor", "dest-filename": "hermit-abi-0.1.8.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%221010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/hermit-abi-0.1.8", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/lazy_static/lazy_static-1.4.0.crate", "sha256": "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646", "dest": "cargo/vendor", "dest-filename": "lazy_static-1.4.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/lazy_static-1.4.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/libc/libc-0.2.67.crate", "sha256": "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018", "dest": "cargo/vendor", "dest-filename": "libc-0.2.67.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/libc-0.2.67", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/maybe-uninit/maybe-uninit-2.0.0.crate", "sha256": "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00", "dest": "cargo/vendor", "dest-filename": "maybe-uninit-2.0.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2260302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/maybe-uninit-2.0.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/memoffset/memoffset-0.5.3.crate", "sha256": "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9", "dest": "cargo/vendor", "dest-filename": "memoffset-0.5.3.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2275189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/memoffset-0.5.3", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/ntapi/ntapi-0.3.3.crate", "sha256": "f26e041cd983acbc087e30fcba770380cfa352d0e392e175b2344ebaf7ea0602", "dest": "cargo/vendor", "dest-filename": "ntapi-0.3.3.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22f26e041cd983acbc087e30fcba770380cfa352d0e392e175b2344ebaf7ea0602%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/ntapi-0.3.3", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/num_cpus/num_cpus-1.12.0.crate", "sha256": "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6", "dest": "cargo/vendor", "dest-filename": "num_cpus-1.12.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2246203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/num_cpus-1.12.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/once_cell/once_cell-1.3.1.crate", "sha256": "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b", "dest": "cargo/vendor", "dest-filename": "once_cell-1.3.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/once_cell-1.3.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/pango/pango-0.8.0.crate", "sha256": "1e9c6b728f1be8edb5f9f981420b651d5ea30bdb9de89f1f1262d0084a020577", "dest": "cargo/vendor", "dest-filename": "pango-0.8.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%221e9c6b728f1be8edb5f9f981420b651d5ea30bdb9de89f1f1262d0084a020577%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/pango-0.8.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/pango-sys/pango-sys-0.9.1.crate", "sha256": "86b93d84907b3cf0819bff8f13598ba72843bee579d5ebc2502e4b0367b4be7d", "dest": "cargo/vendor", "dest-filename": "pango-sys-0.9.1.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2286b93d84907b3cf0819bff8f13598ba72843bee579d5ebc2502e4b0367b4be7d%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/pango-sys-0.9.1", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/pin-utils/pin-utils-0.1.0-alpha.4.crate", "sha256": "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587", "dest": "cargo/vendor", "dest-filename": "pin-utils-0.1.0-alpha.4.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%225894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/pin-utils-0.1.0-alpha.4", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/pkg-config/pkg-config-0.3.17.crate", "sha256": "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677", "dest": "cargo/vendor", "dest-filename": "pkg-config-0.3.17.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2205da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/pkg-config-0.3.17", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/proc-macro-hack/proc-macro-hack-0.5.11.crate", "sha256": "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5", "dest": "cargo/vendor", "dest-filename": "proc-macro-hack-0.5.11.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/proc-macro-hack-0.5.11", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/proc-macro-nested/proc-macro-nested-0.1.3.crate", "sha256": "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e", "dest": "cargo/vendor", "dest-filename": "proc-macro-nested-0.1.3.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/proc-macro-nested-0.1.3", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/proc-macro2/proc-macro2-1.0.9.crate", "sha256": "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435", "dest": "cargo/vendor", "dest-filename": "proc-macro2-1.0.9.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%226c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/proc-macro2-1.0.9", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/quote/quote-1.0.3.crate", "sha256": "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f", "dest": "cargo/vendor", "dest-filename": "quote-1.0.3.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%222bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/quote-1.0.3", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/rayon/rayon-1.3.0.crate", "sha256": "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098", "dest": "cargo/vendor", "dest-filename": "rayon-1.3.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/rayon-1.3.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/rayon-core/rayon-core-1.7.0.crate", "sha256": "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9", "dest": "cargo/vendor", "dest-filename": "rayon-core-1.7.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%2208a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/rayon-core-1.7.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/rustc_version/rustc_version-0.2.3.crate", "sha256": "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a", "dest": "cargo/vendor", "dest-filename": "rustc_version-0.2.3.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/rustc_version-0.2.3", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/scopeguard/scopeguard-1.1.0.crate", "sha256": "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd", "dest": "cargo/vendor", "dest-filename": "scopeguard-1.1.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/scopeguard-1.1.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/semver/semver-0.9.0.crate", "sha256": "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403", "dest": "cargo/vendor", "dest-filename": "semver-0.9.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%221d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/semver-0.9.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/semver-parser/semver-parser-0.7.0.crate", "sha256": "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3", "dest": "cargo/vendor", "dest-filename": "semver-parser-0.7.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/semver-parser-0.7.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/serde/serde-1.0.104.crate", "sha256": "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449", "dest": "cargo/vendor", "dest-filename": "serde-1.0.104.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/serde-1.0.104", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/serde_derive/serde_derive-1.0.104.crate", "sha256": "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64", "dest": "cargo/vendor", "dest-filename": "serde_derive-1.0.104.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/serde_derive-1.0.104", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/slab/slab-0.4.2.crate", "sha256": "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8", "dest": "cargo/vendor", "dest-filename": "slab-0.4.2.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/slab-0.4.2", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/syn/syn-1.0.16.crate", "sha256": "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859", "dest": "cargo/vendor", "dest-filename": "syn-1.0.16.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/syn-1.0.16", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/sysinfo/sysinfo-0.11.7.crate", "sha256": "e15d793f059727ad34a9245503c13b38262bb32e9906d8122ca64d6ca54b0858", "dest": "cargo/vendor", "dest-filename": "sysinfo-0.11.7.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22e15d793f059727ad34a9245503c13b38262bb32e9906d8122ca64d6ca54b0858%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/sysinfo-0.11.7", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/toml/toml-0.5.6.crate", "sha256": "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a", "dest": "cargo/vendor", "dest-filename": "toml-0.5.6.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/toml-0.5.6", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/unicode-xid/unicode-xid-0.2.0.crate", "sha256": "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c", "dest": "cargo/vendor", "dest-filename": "unicode-xid-0.2.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/unicode-xid-0.2.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/winapi/winapi-0.3.8.crate", "sha256": "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6", "dest": "cargo/vendor", "dest-filename": "winapi-0.3.8.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%228093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/winapi-0.3.8", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/winapi-i686-pc-windows-gnu/winapi-i686-pc-windows-gnu-0.4.0.crate", "sha256": "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6", "dest": "cargo/vendor", "dest-filename": "winapi-i686-pc-windows-gnu-0.4.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/winapi-i686-pc-windows-gnu-0.4.0", "dest-filename": ".cargo-checksum.json" }, { "type": "file", "url": "https://static.crates.io/crates/winapi-x86_64-pc-windows-gnu/winapi-x86_64-pc-windows-gnu-0.4.0.crate", "sha256": "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f", "dest": "cargo/vendor", "dest-filename": "winapi-x86_64-pc-windows-gnu-0.4.0.crate" }, { "type": "file", "url": "data:%7B%22package%22%3A%20%22712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f%22%2C%20%22files%22%3A%20%7B%7D%7D", "dest": "cargo/vendor/winapi-x86_64-pc-windows-gnu-0.4.0", "dest-filename": ".cargo-checksum.json" }, { "type": "shell", "dest": "cargo/vendor", "commands": [ "for c in *.crate; do tar -xf $c; done" ] }, { "type": "file", "url": "data:%5Bsource.crates-io%5D%0Areplace-with%20%3D%20%22vendored-sources%22%0A%0A%5Bsource.vendored-sources%5D%0Adirectory%20%3D%20%22cargo/vendor%22%0A", "dest": "cargo", "dest-filename": "config" } ]process_viewer-0.5.9/src/color.rs000064400000000000000000000026551046102023000151610ustar 00000000000000#[derive(Clone, Copy)] pub struct Color { pub r: u8, pub g: u8, pub b: u8, } fn convert(v: u8) -> f32 { f32::from(v) / 255.0 } fn apply(i: isize) -> u8 { let mut value = i - 1; let mut v = 0; for _ in 0..8 { v |= value & 1; v <<= 1; value >>= 1; } v >>= 1; v as u8 } impl Color { pub fn new(r: u8, g: u8, b: u8) -> Color { Color { r, g, b } } pub fn red(self) -> f32 { convert(self.r) } pub fn green(self) -> f32 { convert(self.g) } pub fn blue(self) -> f32 { convert(self.b) } pub fn generate(index: usize) -> Color { let n = (index as f32).cbrt() as isize; let mut index = index as isize - (n * n * n); let p = &mut [n, n, n]; if index == 0 { let r = apply(p[0]); let g = apply(p[1]); let b = apply(p[2]); return Color::new(r, g, b); } index -= 1; let v = (index % 3) as usize; index /= 3; if index < n { p[v] = index % n; let r = apply(p[0]); let g = apply(p[1]); let b = apply(p[2]); return Color::new(r, g, b); } index -= n; p[v] = index / n; p[(v + 1) % 3] = index % n; let r = apply(p[0]); let g = apply(p[1]); let b = apply(p[2]); Color::new(r, g, b) } } process_viewer-0.5.9/src/display_disk.rs000064400000000000000000000060301046102023000165110ustar 00000000000000use std::cell::RefCell; use std::rc::Rc; use crate::utils::format_number; use gtk::glib; use gtk::prelude::*; struct DiskInfo { label: gtk::Label, progress: gtk::ProgressBar, mount_point: String, updated: bool, } fn update_disk(info: &mut DiskInfo, disk: &sysinfo::Disk) { info.label.set_text( format!( "{} mounted on \"{}\"", disk.name().to_str().unwrap_or(""), &info.mount_point, ) .as_str(), ); info.progress.set_text(Some( format!( "{} / {}", format_number(disk.total_space() - disk.available_space()), format_number(disk.total_space()) ) .as_str(), )); info.progress.set_fraction( (disk.total_space() - disk.available_space()) as f64 / disk.total_space() as f64, ); info.updated = true; } fn refresh_disks(container: >k::Box, disks: &[sysinfo::Disk], elems: &mut Vec) { for disk in disks.iter() { let mount_point = disk.mount_point().to_str().unwrap_or(""); update_disk( if let Some(entry) = elems.iter_mut().find(|e| e.mount_point == mount_point) { entry } else { let label = gtk::Label::builder() .margin_top(if elems.is_empty() { 8 } else { 20 }) .build(); let progress = gtk::ProgressBar::new(); progress.set_show_text(true); container.append(&label); container.append(&progress); elems.push(DiskInfo { label, progress, mount_point: mount_point.to_owned(), updated: false, }); elems.last_mut().unwrap() }, disk, ); } for entry in elems.iter().filter(|e| !e.updated) { container.remove(&entry.label); container.remove(&entry.progress); } elems.retain(|e| e.updated); for entry in elems.iter_mut() { entry.updated = false; } } pub fn create_disk_info(stack: >k::Stack) { let elems: Rc>> = Rc::new(RefCell::new(Vec::new())); let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0); let container = gtk::Box::new(gtk::Orientation::Vertical, 0); let scroll = gtk::ScrolledWindow::builder() .hexpand(true) .vexpand(true) .child(&container) .build(); let disks = RefCell::new(sysinfo::Disks::new_with_refreshed_list()); refresh_disks(&container, &disks.borrow(), &mut elems.borrow_mut()); let refresh_but = gtk::Button::with_label("Refresh disks"); refresh_but.connect_clicked(glib::clone!(@weak container, @strong elems => move |_| { disks.borrow_mut().refresh_list(); refresh_disks(&container, &disks.borrow(), &mut elems.borrow_mut()); })); vertical_layout.append(&scroll); vertical_layout.append(&refresh_but); stack.add_titled(&vertical_layout, Some("Disks"), "Disks"); } process_viewer-0.5.9/src/display_network.rs000064400000000000000000000277661046102023000172730ustar 00000000000000use crate::network_dialog::{self, NetworkDialog}; use crate::utils::{format_number, format_number_full}; use gtk::glib; use gtk::prelude::*; use sysinfo::Networks; use std::cell::RefCell; use std::collections::HashSet; use std::rc::Rc; use std::sync::{Arc, Mutex}; fn append_column( title: &str, v: &mut Vec, tree: >k::TreeView, max_width: Option, ) { let id = v.len() as i32; let renderer = gtk::CellRendererText::new(); if title != "name" { renderer.set_xalign(1.0); } let column = gtk::TreeViewColumn::builder() .title(title) .resizable(true) .clickable(true) .min_width(10) .build(); if let Some(max_width) = max_width { column.set_max_width(max_width); column.set_expand(true); } column.pack_start(&renderer, true); column.add_attribute(&renderer, "text", id); tree.append_column(&column); v.push(column); } pub struct Network { list_store: gtk::ListStore, pub search_bar: gtk::SearchBar, dialogs: Rc>>, } impl Network { pub fn new(stack: >k::Stack, networks: &Arc>) -> Self { let tree = gtk::TreeView::builder().headers_visible(true).build(); let scroll = gtk::ScrolledWindow::builder().child(&tree).build(); let info_button = gtk::Button::builder() .label("More information") .hexpand(true) .sensitive(false) .css_classes(vec!["button-with-margin".to_owned()]) .build(); let current_network = Rc::new(RefCell::new(None)); // We put the filter entry at the right bottom. let filter_entry = gtk::SearchEntry::new(); let search_bar = gtk::SearchBar::builder() .halign(gtk::Align::End) .valign(gtk::Align::End) .child(&filter_entry) .show_close_button(true) .build(); let overlay = gtk::Overlay::builder() .child(&scroll) .hexpand(true) .vexpand(true) .build(); overlay.add_overlay(&search_bar); let mut columns: Vec = Vec::new(); let list_store = gtk::ListStore::new(&[ // The first four columns of the model are going to be visible in the view. glib::Type::STRING, // name glib::Type::STRING, // received data glib::Type::STRING, // transmitted data glib::Type::STRING, // received packets glib::Type::STRING, // transmitted packets glib::Type::STRING, // errors on received glib::Type::STRING, // errors on transmitted // These two will serve as keys when sorting by interface name and other numerical // things. glib::Type::STRING, // name_lowercase glib::Type::U64, // received data glib::Type::U64, // transmitted data glib::Type::U64, // received packets glib::Type::U64, // transmitted packets glib::Type::U64, // errors on received glib::Type::U64, // errors on transmitted ]); // The filter model let filter_model = gtk::TreeModelFilter::new(&list_store, None); filter_model.set_visible_func( glib::clone!(@strong filter_entry => @default-return false, move |model, iter| { if !WidgetExt::is_visible(&filter_entry) { return true; } let text = filter_entry.text(); if text.is_empty() { return true; } let text: &str = text.as_ref(); // TODO: Maybe add an option to make searches case sensitive? let name = model.get_value(iter, 0) .get::() .map(|s| s.to_lowercase()) .ok() .unwrap_or_default(); name.contains(text) }), ); // For the filtering to be taken into account, we need to add it directly into the // "global" model. let sort_model = gtk::TreeModelSort::with_model(&filter_model); tree.set_model(Some(&sort_model)); tree.set_search_entry(Some(&filter_entry)); append_column("name", &mut columns, &tree, Some(200)); append_column("received data", &mut columns, &tree, None); append_column("transmitted data", &mut columns, &tree, None); append_column("received packets", &mut columns, &tree, None); append_column("transmitted packets", &mut columns, &tree, None); append_column("errors on received", &mut columns, &tree, None); append_column("errors on transmitted", &mut columns, &tree, None); let columns_len = columns.len(); for (pos, column) in columns.iter().enumerate() { column.set_sort_column_id(pos as i32 + columns_len as i32); } // Sort by network name by default. sort_model.set_sort_column_id( gtk::SortColumn::Index(columns_len as _), gtk::SortType::Ascending, ); let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0); vertical_layout.append(&overlay); vertical_layout.append(&info_button); filter_entry.connect_search_changed(move |_| { filter_model.refilter(); }); stack.add_titled(&vertical_layout, Some("Networks"), "Networks"); tree.connect_cursor_changed( glib::clone!(@weak current_network, @weak info_button => move |tree_view| { let selection = tree_view.selection(); let (name, ret) = if let Some((model, iter)) = selection.selected() { if let Ok(x) = model.get_value(&iter, 0).get::() { (Some(x), true) } else { (None, false) } } else { (None, false) }; *current_network.borrow_mut() = name; info_button.set_sensitive(ret); }), ); let dialogs = Rc::new(RefCell::new(Vec::new())); info_button.connect_clicked(glib::clone!(@weak dialogs, @weak networks => move |_| { let current_network = current_network.borrow(); if let Some(ref interface_name) = *current_network { create_network_dialog( &mut dialogs.borrow_mut(), interface_name, &networks.lock().expect("failed to lock for new network dialog"), ); } })); tree.connect_row_activated( glib::clone!(@weak networks, @weak dialogs => move |tree_view, path, _| { let model = tree_view.model().expect("couldn't get model"); let iter = model.iter(path).expect("couldn't get iter"); let interface_name = model.get_value(&iter, 0) .get::() .expect("Model::get failed"); create_network_dialog( &mut dialogs.borrow_mut(), &interface_name, &networks.lock().expect("failed to lock for new network dialog (from tree)"), ); }), ); Network { list_store, search_bar, dialogs, } } pub fn update_networks(&mut self, networks: &Networks) { // first part, deactivate sorting let sorted = TreeSortableExtManual::sort_column_id(&self.list_store); self.list_store.set_unsorted(); let mut seen: HashSet = HashSet::new(); if let Some(iter) = self.list_store.iter_first() { let mut valid = true; while valid { let name = match self.list_store.get_value(&iter, 0).get::() { Ok(n) => n, _ => { valid = self.list_store.iter_next(&iter); continue; } }; if let Some((_, data)) = networks .iter() .find(|(interface_name, _)| interface_name.as_str() == name) { self.list_store.set( &iter, &[ (1, &format_number(data.received())), (2, &format_number(data.transmitted())), (3, &format_number_full(data.packets_received(), false)), (4, &format_number_full(data.packets_transmitted(), false)), (5, &format_number_full(data.errors_on_received(), false)), (6, &format_number_full(data.errors_on_transmitted(), false)), (8, &data.received()), (9, &data.transmitted()), (10, &data.packets_received()), (11, &data.packets_transmitted()), (12, &data.errors_on_received()), (13, &data.errors_on_transmitted()), ], ); valid = self.list_store.iter_next(&iter); seen.insert(name.to_string()); } else { valid = self.list_store.remove(&iter); } } } for (interface_name, data) in networks.iter() { if !seen.contains(interface_name.as_str()) { create_and_fill_model( &self.list_store, interface_name, data.received(), data.transmitted(), data.packets_received(), data.packets_transmitted(), data.errors_on_received(), data.errors_on_transmitted(), ); } if let Some(dialog) = self .dialogs .borrow() .iter() .find(|x| x.name == *interface_name) { dialog.update(data); } } // we re-enable the sorting if let Some((col, order)) = sorted { self.list_store.set_sort_column_id(col, order); } self.dialogs.borrow_mut().retain(|x| !x.need_remove()); } } #[allow(clippy::too_many_arguments)] fn create_and_fill_model( list_store: >k::ListStore, interface_name: &str, in_usage: u64, out_usage: u64, incoming_packets: u64, outgoing_packets: u64, incoming_errors: u64, outgoing_errors: u64, ) { list_store.insert_with_values( None, &[ (0, &interface_name), (1, &format_number(in_usage)), (2, &format_number(out_usage)), (3, &format_number_full(incoming_packets, false)), (4, &format_number_full(outgoing_packets, false)), (5, &format_number_full(incoming_errors, false)), (6, &format_number_full(outgoing_errors, false)), // sort part (7, &interface_name.to_lowercase()), (8, &in_usage), (9, &out_usage), (10, &incoming_packets), (11, &outgoing_packets), (12, &incoming_errors), (13, &outgoing_errors), ], ); } fn create_network_dialog( dialogs: &mut Vec, interface_name: &str, networks: &Networks, ) { for dialog in dialogs.iter() { if dialog.name == interface_name { dialog.show(); return; } } if let Some((_, data)) = networks .iter() .find(|(name, _)| name.as_str() == interface_name) { dialogs.push(network_dialog::create_network_dialog(data, interface_name)); } else { eprintln!("couldn't find {}...", interface_name); } } process_viewer-0.5.9/src/display_procs.rs000064400000000000000000000216141046102023000167120ustar 00000000000000use gtk::glib; use gtk::prelude::*; use sysinfo::{Pid, Process}; use crate::utils::format_number; use std::cell::Cell; use std::collections::HashMap; use std::rc::Rc; #[allow(dead_code)] pub struct Procs { pub left_tree: gtk::TreeView, pub scroll: gtk::ScrolledWindow, pub current_pid: Rc>>, pub kill_button: gtk::Button, pub info_button: gtk::Button, pub vertical_layout: gtk::Box, pub list_store: gtk::ListStore, pub columns: Vec, pub filter_entry: gtk::SearchEntry, pub search_bar: gtk::SearchBar, } impl Procs { pub fn new(proc_list: &HashMap, stack: >k::Stack) -> Procs { let left_tree = gtk::TreeView::builder().headers_visible(true).build(); let scroll = gtk::ScrolledWindow::builder().child(&left_tree).build(); let current_pid = Rc::new(Cell::new(None)); let kill_button = gtk::Button::builder() .label("End task") .hexpand(true) .margin_top(6) .margin_bottom(6) .margin_end(6) .sensitive(false) .build(); let info_button = gtk::Button::builder() .label("More information") .hexpand(true) .margin_top(6) .margin_bottom(6) .margin_end(6) .margin_start(6) .sensitive(false) .build(); let overlay = gtk::Overlay::builder() .child(&scroll) .hexpand(true) .vexpand(true) .build(); let filter_entry = gtk::SearchEntry::new(); let search_bar = gtk::SearchBar::builder() .halign(gtk::Align::End) .valign(gtk::Align::End) .show_close_button(true) .child(&filter_entry) .build(); // We put the filter entry at the right bottom. overlay.add_overlay(&search_bar); let mut columns: Vec = Vec::new(); let list_store = gtk::ListStore::new(&[ // The first four columns of the model are going to be visible in the view. glib::Type::U32, // pid glib::Type::STRING, // name glib::Type::STRING, // CPU glib::Type::STRING, // mem glib::Type::STRING, // disk I/O // These two will serve as keys when sorting by process name and CPU usage. glib::Type::STRING, // name_lowercase glib::Type::F32, // CPU_f32 glib::Type::U64, // mem glib::Type::U64, // disk I/O ]); for pro in proc_list.values() { if let Some(exe) = pro .exe() .and_then(|exe| exe.file_name().and_then(|f| f.to_str())) .or_else(|| Some(pro.name())) { create_and_fill_model( &list_store, pro.pid().as_u32(), pro.cmd(), exe, pro.cpu_usage(), pro.memory(), ); } } let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0); let horizontal_layout = gtk::Box::new(gtk::Orientation::Horizontal, 6); left_tree.connect_cursor_changed( glib::clone!(@strong current_pid, @weak kill_button, @weak info_button => move |tree_view| { let selection = tree_view.selection(); let (pid, ret) = if let Some((model, iter)) = selection.selected() { if let Ok(x) = model.get_value(&iter, 0).get::() { (Some(Pid::from_u32(x)), true) } else { (None, false) } } else { (None, false) }; current_pid.set(pid); kill_button.set_sensitive(ret); info_button.set_sensitive(ret); }), ); vertical_layout.append(&overlay); horizontal_layout.append(&info_button); horizontal_layout.append(&kill_button); vertical_layout.append(&horizontal_layout); // The filter part. let filter_model = gtk::TreeModelFilter::new(&list_store, None); filter_model.set_visible_func( glib::clone!(@weak filter_entry => @default-return false, move |model, iter| { if !WidgetExt::is_visible(&filter_entry) { return true; } let text = filter_entry.text(); if text.is_empty() { return true; } let text: &str = text.as_ref(); // TODO: Maybe add an option to make searches case sensitive? let pid = model.get_value(iter, 0) .get::() .map(|p| p.to_string()) .ok() .unwrap_or_default(); let name = model.get_value(iter, 1) .get::() .map(|s| s.to_lowercase()) .ok() .unwrap_or_default(); pid.contains(text) || text.contains(&pid) || name.contains(text) || text.contains(&name) }), ); // For the filtering to be taken into account, we need to add it directly into the // "global" model. let sort_model = gtk::TreeModelSort::with_model(&filter_model); left_tree.set_model(Some(&sort_model)); left_tree.set_search_entry(Some(&filter_entry)); append_column("pid", &mut columns, &left_tree, None); append_column("process name", &mut columns, &left_tree, Some(200)); append_column("cpu usage", &mut columns, &left_tree, None); append_column("memory usage", &mut columns, &left_tree, None); #[cfg(not(windows))] { append_column("disk I/O usage", &mut columns, &left_tree, None); } #[cfg(windows)] { append_column("I/O usage", &mut columns, &left_tree, None); } // When we click the "name" column the order is defined by the // "name_lowercase" effectively making the built-in comparator ignore case. columns[1].set_sort_column_id(5); // Likewise clicking the "CPU" column sorts by the "CPU_f32" one because // we want the order to be numerical not lexicographical. columns[2].set_sort_column_id(6); // The memory usage display has been improved, so to make efficient sort, // we have to separate the display and the actual number. columns[3].set_sort_column_id(7); // The disk I/O usage display has been improved, so to make efficient sort, // we have to separate the display and the actual number. columns[4].set_sort_column_id(8); filter_entry.connect_search_changed(move |_| { filter_model.refilter(); }); // Sort by CPU usage by default. sort_model.set_sort_column_id(gtk::SortColumn::Index(6), gtk::SortType::Descending); stack.add_titled(&vertical_layout, Some("Processes"), "Processes"); Procs { left_tree, scroll, current_pid, kill_button, info_button, vertical_layout: vertical_layout .downcast::() .expect("downcast failed"), list_store, columns, filter_entry, search_bar, } } } fn append_column( title: &str, v: &mut Vec, left_tree: >k::TreeView, max_width: Option, ) { let id = v.len() as i32; let renderer = gtk::CellRendererText::new(); if title != "process name" { renderer.set_xalign(1.0); } let column = gtk::TreeViewColumn::builder() .title(title) .resizable(true) .min_width(10) .clickable(true) .sort_column_id(id) .build(); if let Some(max_width) = max_width { column.set_max_width(max_width); column.set_expand(true); } column.pack_start(&renderer, true); column.add_attribute(&renderer, "text", id); left_tree.append_column(&column); v.push(column); } pub fn create_and_fill_model( list_store: >k::ListStore, pid: u32, cmdline: &[String], name: &str, cpu: f32, memory: u64, ) { if cmdline.is_empty() || name.is_empty() { return; } list_store.insert_with_values( None, &[ (0, &pid), (1, &name), (2, &format!("{:.1}", cpu)), (3, &format_number(memory)), (4, &String::new()), (5, &name.to_lowercase()), (6, &cpu), (7, &memory), (8, &0), ], ); } process_viewer-0.5.9/src/display_sysinfo.rs000064400000000000000000000335721046102023000172640ustar 00000000000000use gtk::glib; use gtk::prelude::*; use std::cell::RefCell; use std::iter; use std::rc::Rc; use std::sync::{Arc, Mutex}; use crate::graph::GraphWidget; use crate::settings::Settings; use crate::utils::{format_number, graph_label_units, RotateVec}; pub fn create_header( label_text: &str, parent_layout: >k::Box, display_graph: bool, ) -> gtk::CheckButton { let check_box = gtk::CheckButton::builder() .label("Graph view") .active(display_graph) .halign(gtk::Align::End) .build(); let label = gtk::Label::new(Some(label_text)); let grid = gtk::Grid::builder() .hexpand(true) .column_homogeneous(true) .build(); grid.attach(>k::Label::new(None), 0, 0, 2, 1); // needed otherwise it won't take space grid.attach(&label, 1, 0, 2, 1); grid.attach(&check_box, 3, 0, 1, 1); parent_layout.append(&grid); check_box } pub fn create_progress_bar( non_graph_layout: >k::Grid, line: i32, label: &str, text: &str, ) -> gtk::ProgressBar { let p = gtk::ProgressBar::builder() .text(text) .show_text(true) .build(); let l = gtk::Label::new(Some(label)); non_graph_layout.attach(&l, 0, line, 1, 1); non_graph_layout.attach(&p, 1, line, 11, 1); p } #[allow(dead_code)] pub struct DisplaySysInfo { procs: Rc>>, ram: gtk::ProgressBar, swap: gtk::ProgressBar, vertical_layout: gtk::Box, components: Vec, cpu_usage_history: Rc>, // 0 = RAM // 1 = SWAP ram_usage_history: Rc>, temperature_usage_history: Rc>, pub ram_check_box: gtk::CheckButton, pub swap_check_box: gtk::CheckButton, pub temperature_check_box: Option, } impl DisplaySysInfo { pub fn new( sys: &Arc>, sys_components: &sysinfo::Components, stack: >k::Stack, settings: &Settings, ) -> DisplaySysInfo { let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0); let mut procs = Vec::new(); let scroll = gtk::ScrolledWindow::new(); let mut components = vec![]; // CPU let cpu_usage_history = GraphWidget::new(None, false); cpu_usage_history.set_margin_start(3); cpu_usage_history.set_margin_end(6); cpu_usage_history.set_labels_callback(Some(Box::new(|_| { [ "100".to_string(), "50".to_string(), "0".to_string(), "%".to_string(), ] }))); let sys = sys.lock().expect("failed to lock in DisplaySysInfo::new"); // RAM let ram_usage_history = GraphWidget::new(Some(sys.total_memory() as f32), true); ram_usage_history.set_margin_start(3); ram_usage_history.set_margin_end(6); ram_usage_history.set_labels_callback(Some(Box::new(graph_label_units))); // TEMPERATURE let temperature_usage_history = GraphWidget::new(Some(1.), false); temperature_usage_history.set_margin_start(3); temperature_usage_history.set_margin_end(6); temperature_usage_history.set_overhead(Some(20.)); temperature_usage_history.set_labels_callback(Some(Box::new(|v| { [ format!("{:.1}", v), format!("{:.1}", v / 2.), "0".to_string(), "°C".to_string(), ] }))); let mut check_box3 = None; vertical_layout.set_spacing(5); vertical_layout.set_margin_top(10); vertical_layout.set_margin_bottom(10); let non_graph_layout = gtk::Grid::builder() .column_homogeneous(true) .margin_end(5) .build(); let non_graph_layout2 = gtk::Grid::builder() .column_homogeneous(true) .margin_start(5) .build(); let non_graph_layout3 = gtk::Box::new(gtk::Orientation::Vertical, 0); // // PROCESSOR PART // let label = gtk::Label::new(Some("Total CPU usage")); label.set_margin_start(7); vertical_layout.append(&label); procs.push(gtk::ProgressBar::new()); { procs.push(gtk::ProgressBar::new()); let p: >k::ProgressBar = &procs[0]; p.set_margin_end(5); p.set_margin_start(5); p.set_show_text(true); let processor = sys.global_cpu_info(); p.set_text(Some(&format!("{:.1} %", processor.cpu_usage()))); p.set_fraction(f64::from(processor.cpu_usage() / 100.)); vertical_layout.append(p); } let check_box = create_header("Processors usage", &vertical_layout, settings.display_graph); for (i, pro) in sys.cpus().iter().enumerate() { procs.push(gtk::ProgressBar::new()); let p: >k::ProgressBar = &procs[i + 1]; let l = gtk::Label::new(Some(&format!("{}", i))); p.set_text(Some(&format!("{:.1} %", pro.cpu_usage()))); p.set_show_text(true); p.set_fraction(f64::from(pro.cpu_usage())); non_graph_layout.attach(&l, 0, i as i32 - 1, 1, 1); non_graph_layout.attach(p, 1, i as i32 - 1, 11, 1); cpu_usage_history.push( RotateVec::new(iter::repeat(0f32).take(61).collect()), &format!("processor {}", i), None, ); } vertical_layout.append(&non_graph_layout); vertical_layout.append(&cpu_usage_history); // // MEMORY PART // let check_box2 = create_header("Memory usage", &vertical_layout, settings.display_graph); let ram = create_progress_bar(&non_graph_layout2, 0, "RAM", ""); let swap = create_progress_bar(&non_graph_layout2, 1, "Swap", ""); non_graph_layout2.set_margin_start(15); vertical_layout.append(&non_graph_layout2); //vertical_layout.append(&non_graph_layout2); ram_usage_history.push( RotateVec::new(iter::repeat(0f32).take(61).collect()), "RAM", Some(4), ); ram_usage_history.push( RotateVec::new(iter::repeat(0f32).take(61).collect()), "Swap", Some(2), ); vertical_layout.append(&ram_usage_history); // // TEMPERATURES PART // if !sys_components.is_empty() { check_box3 = Some(create_header( "Components' temperature", &vertical_layout, settings.display_graph, )); for component in sys_components { let horizontal_layout = gtk::Box::new(gtk::Orientation::Horizontal, 10); // TODO: add max and critical temperatures as well let temp = gtk::Label::new(Some(&format!("{:.1} °C", component.temperature()))); horizontal_layout.append(>k::Label::new(Some(component.label()))); horizontal_layout.append(&temp); horizontal_layout.set_homogeneous(true); non_graph_layout3.append(&horizontal_layout); components.push(temp); temperature_usage_history.push( RotateVec::new(iter::repeat(0f32).take(61).collect()), component.label(), None, ); } vertical_layout.append(&non_graph_layout3); vertical_layout.append(&temperature_usage_history); } // // Putting everyting into places now. // let cpu_usage_history = Rc::new(RefCell::new(cpu_usage_history)); let ram_usage_history = Rc::new(RefCell::new(ram_usage_history)); let temperature_usage_history = Rc::new(RefCell::new(temperature_usage_history)); scroll.set_child(Some(&vertical_layout)); stack.add_titled(&scroll, Some("System"), "System"); cpu_usage_history.borrow().hide(); ram_usage_history.borrow().hide(); temperature_usage_history.borrow().hide(); check_box.connect_toggled( glib::clone!(@weak non_graph_layout, @weak cpu_usage_history => move |c| { show_if_necessary(c, &cpu_usage_history.borrow(), &non_graph_layout); }), ); // To show the correct view based on the saved settings. show_if_necessary(&check_box, &cpu_usage_history.borrow(), &non_graph_layout); check_box2.connect_toggled( glib::clone!(@weak non_graph_layout2, @weak ram_usage_history => move |c| { show_if_necessary(c, &ram_usage_history.borrow(), &non_graph_layout2); }), ); // To show the correct view based on the saved settings. show_if_necessary(&check_box2, &ram_usage_history.borrow(), &non_graph_layout2); if let Some(ref check_box3) = check_box3 { check_box3.connect_toggled( glib::clone!(@weak non_graph_layout3, @weak temperature_usage_history => move |c| { show_if_necessary(c, &temperature_usage_history.borrow(), &non_graph_layout3); }), ); // To show the correct view based on the saved settings. show_if_necessary( check_box3, &temperature_usage_history.borrow(), &non_graph_layout3, ); } let mut tmp = DisplaySysInfo { procs: Rc::new(RefCell::new(procs)), ram, swap, vertical_layout, components, cpu_usage_history, ram_usage_history, ram_check_box: check_box, swap_check_box: check_box2, temperature_usage_history, temperature_check_box: check_box3, }; tmp.update_system_info(&sys, sys_components, settings.display_fahrenheit); tmp } pub fn set_checkboxes_state(&self, active: bool) { self.ram_check_box.set_active(active); self.swap_check_box.set_active(active); if let Some(ref temperature_check_box) = self.temperature_check_box { temperature_check_box.set_active(active); } } pub fn update_system_info( &mut self, sys: &sysinfo::System, sys_components: &sysinfo::Components, display_fahrenheit: bool, ) { let disp = |total, used| { format!( "{} / {}", format_number(used), format_number(total) // We need to multiply to get the "right" unit. ) }; let total_ram = sys.total_memory(); let used = sys.used_memory(); self.ram.set_text(Some(&disp(total_ram, used))); if total_ram != 0 { self.ram.set_fraction(used as f64 / total_ram as f64); } else { self.ram.set_fraction(0.0); } { let r = self.ram_usage_history.borrow_mut(); r.data(0, |d| { d.move_start(); if let Some(p) = d.get_mut(0) { *p = used as _; } }); } let total = ::std::cmp::max(sys.total_swap(), total_ram); let used = sys.used_swap(); self.swap.set_text(Some(&disp(sys.total_swap(), used))); let mut fraction = if total != 0 { used as f64 / total as f64 } else { 0f64 }; if fraction.is_nan() { fraction = 0.; } self.swap.set_fraction(fraction); { let r = self.ram_usage_history.borrow_mut(); r.data(1, |d| { d.move_start(); if let Some(p) = d.get_mut(0) { *p = used as _; } }); } // temperature part let t = self.temperature_usage_history.borrow_mut(); for (pos, (component, label)) in sys_components .iter() .zip(self.components.iter()) .enumerate() { t.data(pos, |d| { d.move_start(); if let Some(t) = d.get_mut(0) { *t = component.temperature(); } if let Some(t) = d.get_mut(0) { *t = component.temperature(); } }); if display_fahrenheit { label.set_text(&format!("{:.1} °F", component.temperature() * 1.8 + 32.)); } else { label.set_text(&format!("{:.1} °C", component.temperature())); } } } pub fn update_system_info_display(&mut self, sys: &sysinfo::System) { let v = &*self.procs.borrow_mut(); let h = &mut *self.cpu_usage_history.borrow_mut(); v[0].set_text(Some(&format!("{:.1} %", sys.global_cpu_info().cpu_usage()))); v[0].set_show_text(true); v[0].set_fraction(f64::from(sys.global_cpu_info().cpu_usage() / 100.)); for (i, pro) in sys.cpus().iter().enumerate() { let i = i + 1; v[i].set_text(Some(&format!("{:.1} %", pro.cpu_usage()))); v[i].set_show_text(true); v[i].set_fraction(f64::from(pro.cpu_usage() / 100.)); h.data(i - 1, |d| { d.move_start(); if let Some(h) = d.get_mut(0) { *h = pro.cpu_usage() / 100.; } }); } h.queue_draw(); self.ram_usage_history.borrow().queue_draw(); self.temperature_usage_history.borrow().queue_draw(); } } pub fn show_if_necessary, T: WidgetExt>( check_box: &U, proc_horizontal_layout: &GraphWidget, non_graph_layout: &T, ) { if check_box.is_active() { proc_horizontal_layout.show(); non_graph_layout.hide(); } else { non_graph_layout.show(); proc_horizontal_layout.hide(); } } process_viewer-0.5.9/src/graph.rs000064400000000000000000000402101046102023000151310ustar 00000000000000use gdk::RGBA; use graphene::Rect; use gsk::RoundedRect; use gtk::prelude::*; use gtk::subclass::prelude::*; use gtk::{cairo, gdk, glib, graphene, gsk}; use std::cell::{Cell, RefCell}; use crate::color::Color; use crate::utils::RotateVec; const LEFT_WIDTH: f32 = 31.; const HEIGHT: f32 = 200.; glib::wrapper! { pub struct GraphWidget(ObjectSubclass) @extends gtk::Widget; } impl GraphWidget { /// If `max` is `None`, the graph will expect values between 0 and 1. /// /// If `keep_max` is set to `true`, then this value will never go down, meaning that graphs /// won't rescale down. It is not taken into account if `max` is `None`. pub fn new(max: Option, keep_max: bool) -> Self { let widget = glib::Object::new::(); widget.imp().graph.borrow().set_max(max); widget.imp().graph.borrow().set_keep_max(keep_max); widget.imp().graph.borrow().set_hexpand(true); widget } pub fn set_labels_callback(&self, labels_callback: Option [String; 4]>>) { self.imp() .graph .borrow() .set_labels_callback(labels_callback); } pub fn push(&self, d: RotateVec, s: &str, override_color: Option) { let color = self.imp().graph.borrow().push(d, override_color); let layout = gtk::Box::new(gtk::Orientation::Horizontal, 0); let square = SquareWidget::new(color); square.set_margin_end(5); let l = gtk::Label::new(Some(s)); layout.append(&square); layout.append(&l); self.imp().labels.borrow().insert(&layout, -1); } pub fn data)>(&self, pos: usize, f: F) { self.imp().graph.borrow().data(pos, f); } pub fn set_overhead(&self, overhead: Option) { self.imp().graph.borrow().set_overhead(overhead); } pub fn set_minimum(&self, minimum: Option) { self.imp().graph.borrow().set_minimum(minimum); } pub fn set_display_labels(&self, display_labels: bool) { self.imp().display_labels.set(display_labels); if display_labels { self.imp().labels.borrow().show(); } else { self.imp().labels.borrow().hide(); } } } pub struct GraphWidgetImp { graph: RefCell, labels: RefCell, display_labels: Cell, } impl Default for GraphWidgetImp { fn default() -> Self { Self { graph: RefCell::new(GraphInnerWidget::new()), labels: RefCell::new(gtk::FlowBox::new()), display_labels: Cell::new(true), } } } #[glib::object_subclass] impl ObjectSubclass for GraphWidgetImp { const NAME: &'static str = "GraphWidgetImp"; type Type = GraphWidget; type ParentType = gtk::Widget; fn class_init(klass: &mut Self::Class) { klass.set_layout_manager_type::(); } } impl WidgetImpl for GraphWidgetImp { fn show(&self) { self.parent_show(); if !self.display_labels.get() { self.labels.borrow().hide(); } } } impl ObjectImpl for GraphWidgetImp { fn constructed(&self) { self.parent_constructed(); let obj = self.obj(); let layout = obj .layout_manager() .and_downcast::() .unwrap(); layout.set_orientation(gtk::Orientation::Vertical); layout.set_spacing(5); self.labels.borrow().set_homogeneous(true); self.graph.borrow().set_parent(&*obj); self.labels.borrow().set_parent(&*obj); } fn dispose(&self) { // Child widgets need to be manually unparented in `dispose()`. self.graph.borrow().unparent(); self.labels.borrow().unparent(); } } glib::wrapper! { pub struct GraphInnerWidget(ObjectSubclass) @extends gtk::Widget; } impl GraphInnerWidget { #[allow(clippy::new_without_default)] pub fn new() -> Self { glib::Object::new() } pub fn set_max(&self, max: Option) { self.imp().max.set(max); } pub fn set_keep_max(&self, keep_max: bool) { self.imp().keep_max.set(keep_max); } pub fn set_minimum(&self, minimum: Option) { self.imp().minimum.set(minimum); } pub fn set_overhead(&self, overhead: Option) { if let Some(o) = overhead { assert!(o >= 0.); } self.imp().overhead.set(overhead); } pub fn set_labels_callback(&self, labels_callback: Option [String; 4]>>) { *self.imp().labels_callback.borrow_mut() = labels_callback; } pub fn attach_to(&self, to: >k::Box) { to.append(self); } pub fn push(&self, d: RotateVec, override_color: Option) -> Color { let c = if let Some(over) = override_color { Color::generate(over) } else { Color::generate(self.imp().data.borrow().len() + 11) }; self.imp().colors.borrow_mut().push(c); self.imp().data.borrow_mut().push(d); c } pub fn data)>(&self, pos: usize, mut f: F) { f(&mut self.imp().data.borrow_mut()[pos]); self.queue_draw(); } } pub struct GraphPainter { colors: RefCell>, data: RefCell>>, max: Cell>, keep_max: Cell, /// `minimum` is used only if `max` is set: it'll be the minimum that the `max` value will /// be able to go down. minimum: Cell>, // In %, from 0 to whatever overhead: Cell>, #[allow(clippy::type_complexity)] labels_callback: RefCell [String; 4]>>>, } impl Default for GraphPainter { fn default() -> Self { Self { colors: RefCell::new(Vec::new()), data: RefCell::new(Vec::new()), max: Cell::new(None), keep_max: Cell::new(false), minimum: Cell::new(None), overhead: Cell::new(None), // need_label_update: Cell::new(true), labels_callback: RefCell::new(None), } } } #[glib::object_subclass] impl ObjectSubclass for GraphPainter { const NAME: &'static str = "GraphPainter"; type Type = GraphInnerWidget; type ParentType = gtk::Widget; fn class_init(klass: &mut Self::Class) { klass.set_css_name("graph_widget"); } } impl GraphPainter { fn draw_labels(&self, widget: &GraphInnerWidget, c: &cairo::Context, max: f32) { if let Some(ref call) = *self.labels_callback.borrow() { let entries = call(max); let font_size = 8.; let left_width = LEFT_WIDTH as f64; let height = HEIGHT as f64; let color = widget.style_context().color(); c.set_source_rgba( color.red() as _, color.green() as _, color.blue() as _, color.alpha() as _, ); c.set_font_size(font_size); c.move_to(left_width - 4. - entries[0].len() as f64 * 4., font_size); let _ = c.show_text(entries[0].as_str()); c.move_to(left_width - 4. - entries[1].len() as f64 * 4., height / 2.); let _ = c.show_text(entries[1].as_str()); c.move_to(left_width - 4. - entries[2].len() as f64 * 4., height - 2.); let _ = c.show_text(entries[2].as_str()); c.move_to( font_size + 1., height / 2. + 4. * (entries[3].len() / 2) as f64, ); c.rotate(-::std::f64::consts::FRAC_PI_2); let _ = c.show_text(entries[3].as_str()); // *Better* code that should be used but crashes at `sub_snap.to_node()`. // let ctx = widget.create_pango_context(); // let font_description = ctx.font_description().unwrap(); // font_description.set_size (font_size * pango::SCALE); // ctx.set_font_description(&font_description); // let entries = call(max); // let layout = pango::Layout::new(&ctx); // layout.set_text(&entries[0]); // snapshot.render_layout(&ctx, LEFT_WIDTH - 4. - (entries[0].len() * 4) as _, font_size); // let layout = pango::Layout::new(&ctx); // layout.set_text(&entries[1]); // snapshot.render_layout(&ctx, LEFT_WIDTH - 4. - (entries[1].len() * 4) as _, HEIGHT / 2.); // let layout = pango::Layout::new(&ctx); // layout.set_text(&entries[2]); // snapshot.render_layout(&ctx, LEFT_WIDTH - 4. - (entries[2].len() * 4) as _, HEIGHT - 2.); // let sub_snap = gtk::Snapshot::new(); // let layout = pango::Layout::new(&ctx); // layout.set_text(&entries[3]); // sub_snap.render_layout(&ctx, font_size - 1., HEIGHT / 2. + 4. * (entries[3].len() / 2) as _); // sub_snap.rotate(-90.); // snapshot.append_node(sub_snap.to_node().unwrap()); } } } impl ObjectImpl for GraphPainter {} impl WidgetImpl for GraphPainter { fn measure(&self, orientation: gtk::Orientation, _for_size: i32) -> (i32, i32, i32, i32) { if orientation == gtk::Orientation::Vertical { // Minimum height is HEIGHT. (HEIGHT as i32, HEIGHT as i32, -1, -1) } else { // Minimum width is 50. (50, 50, -1, -1) } } fn snapshot(&self, snapshot: >k::Snapshot) { let widget = self.obj(); let x_start = if self.labels_callback.borrow().is_some() { LEFT_WIDTH } else { 0. }; let width = widget.width() as f32 - x_start - 2.; // to limit line "fuzziness" #[inline] fn rounder(x: f32) -> f32 { let fract = x.fract(); if fract < 0.5 { x.trunc() + 0.5 } else { x.trunc() + 1.5 } } snapshot.append_border( &RoundedRect::from_rect(Rect::new(x_start, 0., width + 2., HEIGHT), 0.), &[1., 1., 1., 1.], &[RGBA::WHITE, RGBA::WHITE, RGBA::WHITE, RGBA::WHITE], ); snapshot.append_color( &RGBA::BLACK, &Rect::new(x_start + 1., 1., width, HEIGHT - 2.), ); let color = RGBA::new(0.5, 0.5, 0.5, 1.); // We always draw 10 lines (12 if we count the borders). let x_step = width / 12.; let mut current = width - width / 12. + x_start + 1.; if x_step < 0.1 { return; } while current > x_start { snapshot.append_color(&color, &Rect::new(current, 1., 1., HEIGHT - 2.)); current -= x_step; } let step = HEIGHT / 10.; current = step - 1.0; while current < HEIGHT - 1. { let y = rounder(current) - 1.; snapshot.append_color(&color, &Rect::new(x_start + 1., y, width, 1.)); current += step; } if let Some(self_max) = self.max.get() { let data = self.data.borrow(); let mut max = if self.keep_max.get() { self_max } else { 1. }; if !data.is_empty() && !data[0].is_empty() { let len = data[0].len() - 1; for x in 0..len { for entry in &*self.data.borrow() { if entry[x] > max { max = entry[x]; } } } if let Some(min) = self.minimum.get() { if min > max { max = min; } } else if let Some(over) = self.overhead.get() { max = max + max * over / 100.; } let step = width / len as f32; let c = snapshot.append_cairo(&Rect::new(0., 1., width + 1. + x_start, HEIGHT - 2.)); current = x_start + 2.0; let colors = self.colors.borrow(); let mut index = len; while current > 0. && index > 0 { for (entry, color) in data.iter().zip(colors.iter()) { c.set_source_rgb(color.r as _, color.g as _, color.b as _); c.move_to( (current + step) as f64, (HEIGHT - entry[index - 1] / max * (HEIGHT - 1.0)) as f64, ); c.line_to( current as f64, (HEIGHT - entry[index] / max * (HEIGHT - 1.0)) as f64, ); let _ = c.stroke(); } current += step; index -= 1; } if max > self_max || !self.keep_max.get() { self.max.set(Some(max)); } } let c = snapshot.append_cairo(&Rect::new(1., 0., x_start, HEIGHT - 2.)); self.draw_labels(&widget, &c, max); } else { let data = self.data.borrow(); if !data.is_empty() && !data[0].is_empty() { let c = snapshot.append_cairo(&Rect::new(0., 1., width + 1. + x_start, HEIGHT - 2.)); let len = data[0].len() - 1; let step = width / (len as f32); current = x_start + 2.0; let mut index = len; let colors = self.colors.borrow(); while current > 0. && index > 0 { for (entry, color) in data.iter().zip(colors.iter()) { c.set_source_rgb(color.r as _, color.g as _, color.b as _); c.move_to( (current + step) as f64, (HEIGHT - entry[index - 1] * (HEIGHT - 1.0)) as f64, ); c.line_to( current as f64, (HEIGHT - entry[index] * (HEIGHT - 1.0)) as f64, ); let _ = c.stroke(); } current += step; index -= 1; } } let c = snapshot.append_cairo(&Rect::new(1., 0., x_start, HEIGHT)); // To be called in last to avoid having to restore state (rotation). self.draw_labels(&widget, &c, 100.); } } } glib::wrapper! { pub struct SquareWidget(ObjectSubclass) @extends gtk::Widget; } impl SquareWidget { pub fn new(color: Color) -> Self { let widget = glib::Object::new::(); widget.imp().color.set(color); widget } } pub struct SquarePainter { color: Cell, } impl Default for SquarePainter { fn default() -> Self { Self { color: Cell::new(Color::new(0, 0, 0)), } } } #[glib::object_subclass] impl ObjectSubclass for SquarePainter { const NAME: &'static str = "SquarePainter"; type Type = SquareWidget; type ParentType = gtk::Widget; fn class_init(_klass: &mut Self::Class) {} } impl ObjectImpl for SquarePainter {} impl WidgetImpl for SquarePainter { fn measure(&self, _orientation: gtk::Orientation, _for_size: i32) -> (i32, i32, i32, i32) { // Minimum width is 20. (20, 20, -1, -1) } fn request_mode(&self) -> gtk::SizeRequestMode { gtk::SizeRequestMode::WidthForHeight } fn snapshot(&self, snapshot: >k::Snapshot) { let widget = self.obj(); let width = widget.width() as f32; let height = widget.height() as f32; let margin = 2.; // only to limit the height snapshot.append_border( &RoundedRect::from_rect(Rect::new(0., margin, width, height - margin * 2.), 0.), &[1., 1., 1., 1.], &[RGBA::WHITE, RGBA::WHITE, RGBA::WHITE, RGBA::WHITE], ); let color = self.color.get(); snapshot.append_color( &RGBA::new(color.red(), color.green(), color.blue(), 1.), &Rect::new(1., margin + 1., width - 2., height - 2. - margin * 2.), ); } } process_viewer-0.5.9/src/main.rs000075500000000000000000000600531046102023000147660ustar 00000000000000// // Process viewer // // Copyright (c) 2017 Guillaume Gomez // #![crate_type = "bin"] use gdk::Texture; use gdk_pixbuf::Pixbuf; use gio::MemoryInputStream; use glib::Bytes; use gtk::prelude::*; use gtk::{gdk, gdk_pixbuf, gio, glib}; use gtk::{AboutDialog, Dialog, Entry, MessageDialog}; use sysinfo::{Networks, Pid, RefreshKind}; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; #[cfg(unix)] use std::os::unix::process::CommandExt; use std::process::{Command, Stdio}; use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; mod color; mod display_disk; #[macro_use] mod display_sysinfo; mod display_network; mod display_procs; mod graph; mod network_dialog; mod notebook; mod process_dialog; mod settings; mod utils; use display_network::Network; use display_procs::{create_and_fill_model, Procs}; use display_sysinfo::DisplaySysInfo; use settings::Settings; use utils::format_number; pub const APPLICATION_NAME: &str = "fr.guillaume_gomez.ProcessViewer"; fn update_window(list: >k::ListStore, entries: &HashMap) { let mut seen: HashSet = HashSet::new(); if let Some(iter) = list.iter_first() { let mut valid = true; while valid { let pid = match list.get_value(&iter, 0).get::() { Ok(pid) => Pid::from_u32(pid), _ => { valid = list.iter_next(&iter); continue; } }; if let Some(p) = entries.get(&(pid)) { let disk_usage = p.disk_usage(); let disk_usage = disk_usage.written_bytes + disk_usage.read_bytes; let memory = p.memory(); list.set( &iter, &[ (2, &format!("{:.1}", p.cpu_usage())), (3, &format_number(memory)), ( 4, &if disk_usage > 0 { format_number(disk_usage) } else { String::new() }, ), (6, &p.cpu_usage()), (7, &memory), (8, &disk_usage), ], ); valid = list.iter_next(&iter); seen.insert(pid); } else { valid = list.remove(&iter); } } } for (pid, pro) in entries.iter() { if !seen.contains(pid) { create_and_fill_model( list, pid.as_u32(), pro.cmd(), pro.name(), pro.cpu_usage(), pro.memory(), ); } } } fn parse_quote(line: &str, quote: char) -> Vec { let args = line.split(quote).collect::>(); let mut out_args = vec![]; for (num, arg) in args.iter().enumerate() { if num != 1 { out_args.extend_from_slice(&parse_entry(arg)); } else { out_args.push((*arg).to_owned()); } } out_args } // super simple parsing fn parse_entry(line: &str) -> Vec { match (line.find('\''), line.find('"')) { (Some(x), Some(y)) => { if x < y { parse_quote(line, '\'') } else { parse_quote(line, '"') } } (Some(_), None) => parse_quote(line, '\''), (None, Some(_)) => parse_quote(line, '"'), (None, None) => line .split(' ') .map(::std::borrow::ToOwned::to_owned) .collect::>(), } } #[cfg(unix)] fn build_command(c: &mut Command) -> &mut Command { unsafe { c.pre_exec(|| { libc::setsid(); Ok(()) }) } } #[cfg(windows)] fn build_command(c: &mut Command) -> &mut Command { c } fn start_detached_process(line: &str) -> Option { let args = parse_entry(line); let command = args[0].clone(); let cmd = build_command(Command::new(&command).args(&args)) .stdin(Stdio::null()) .stderr(Stdio::null()) .stdout(Stdio::null()) .spawn(); if cmd.is_err() { Some(format!("Failed to start '{}'", &command)) } else { None } } fn run_command>(input: &Entry, window: &T, d: &Dialog) { let text = input.text(); let x = if let Some(x) = start_detached_process(&text) { x } else { "The command started successfully".to_owned() }; d.close(); let m = MessageDialog::new( Some(window), gtk::DialogFlags::DESTROY_WITH_PARENT, gtk::MessageType::Info, gtk::ButtonsType::Ok, x, ); m.set_modal(true); m.connect_response(|dialog, response| { if response == gtk::ResponseType::DeleteEvent || response == gtk::ResponseType::Close || response == gtk::ResponseType::Ok { dialog.close(); } }); m.show(); } fn create_new_proc_diag( process_dialogs: &Rc>>, pid: Pid, sys: &sysinfo::System, ) { if let Some(proc_diag) = process_dialogs .borrow() .iter() .filter(|x| !x.is_dead) .find(|x| x.pid == pid) { proc_diag.popup.present(); return; } let total_memory = sys.total_memory(); if let Some(process) = sys.process(pid) { process_dialogs .borrow_mut() .push(process_dialog::create_process_dialog(process, total_memory)); } } #[derive(Clone)] pub struct RequiredForSettings { process_refresh_timeout: Arc>, network_refresh_timeout: Arc>, system_refresh_timeout: Arc>, sys: Arc>, process_dialogs: Rc>>, list_store: gtk::ListStore, display_tab: Rc>, network_tab: Rc>, } fn setup_timeout(rfs: &RequiredForSettings) { let (sender, receiver) = async_channel::bounded(1); let sys = &rfs.sys; let process_dialogs = &rfs.process_dialogs; let list_store = &rfs.list_store; let process_refresh_timeout = &rfs.process_refresh_timeout; thread::spawn( glib::clone!(@weak sys, @weak process_refresh_timeout => move || { loop { let sleep_dur = Duration::from_millis( *process_refresh_timeout.lock().expect("failed to lock process refresh mutex") as _); thread::sleep(sleep_dur); sys.lock().expect("failed to lock to refresh processes").refresh_processes(); sender.send_blocking(()).expect("failed to send data through process refresh channel"); } }), ); glib::spawn_future_local( glib::clone!(@weak sys, @weak list_store, @weak process_dialogs => async move { loop { match receiver.recv().await { Ok(_) => {}, Err(error) => { eprintln!("Stopping process info update loop: {error:?}"); return; } } // first part, deactivate sorting let sorted = TreeSortableExtManual::sort_column_id(&list_store); list_store.set_unsorted(); let mut dialogs = process_dialogs.borrow_mut(); if let Ok(sys) = sys.lock() { // we update the tree view update_window(&list_store, sys.processes()); // we re-enable the sorting if let Some((col, order)) = sorted { list_store.set_sort_column_id(col, order); } for dialog in dialogs.iter_mut().filter(|x| !x.is_dead) { // TODO: check if the process name matches the PID too! if let Some(process) = sys.processes().get(&dialog.pid) { dialog.update(process); } else { dialog.set_dead(); } } dialogs.retain(|x| !x.need_remove()); } } }), ); } fn setup_network_timeout(rfs: &RequiredForSettings, networks: Arc>) { let (sender, receiver) = async_channel::bounded(1); let network_refresh_timeout = &rfs.network_refresh_timeout; let network_tab = &rfs.network_tab; thread::spawn( glib::clone!(@weak networks, @weak network_refresh_timeout => move || { loop { let sleep_dur = Duration::from_millis( *network_refresh_timeout.lock().expect("failed to lock networks refresh mutex") as _); thread::sleep(sleep_dur); networks.lock().expect("failed to lock to refresh networks").refresh_list(); sender.send_blocking(()).expect("failed to send data through networks refresh channel"); } }), ); glib::spawn_future_local(glib::clone!(@weak network_tab => async move { loop { match receiver.recv().await { Ok(_) => {}, Err(error) => { eprintln!("Stopping network info update loop: {error:?}"); return; } } network_tab.borrow_mut().update_networks(&networks.lock().expect("failed to lock to update networks")); } })); } fn setup_system_timeout( rfs: &RequiredForSettings, settings: &Rc>, components: sysinfo::Components, ) { let (sender, receiver) = async_channel::bounded(1); let system_refresh_timeout = &rfs.system_refresh_timeout; let sys = &rfs.sys; let display_tab = &rfs.display_tab; let components = Arc::new(Mutex::new(components)); thread::spawn( glib::clone!(@weak sys, @weak system_refresh_timeout, @weak components => move || { loop { let sleep_dur = Duration::from_millis( *system_refresh_timeout.lock().expect("failed to lock system refresh mutex") as _); thread::sleep(sleep_dur); sys.lock().expect("failed to lock to refresh system").refresh_all(); components.lock().expect("failed to lock components").refresh_list(); sender.send_blocking(()).expect("failed to send data through system refresh channel"); } }), ); glib::spawn_future_local( glib::clone!(@weak sys, @weak display_tab, @weak settings => async move { loop { match receiver.recv().await { Ok(_) => {}, Err(error) => { eprintln!("Stopping system info update loop: {error:?}"); return; } } let mut info = display_tab.borrow_mut(); let sys = sys.lock().expect("failed to lock to update system"); let components = components.lock().expect("failed to lock components"); let display_fahrenheit = settings.borrow().display_fahrenheit; info.update_system_info(&sys, &components, display_fahrenheit); info.update_system_info_display(&sys); } }), ); } fn create_header_bar(stack: >k::Stack) -> (gtk::HeaderBar, gtk::Button) { let header_buttons = gtk::StackSwitcher::new(); header_buttons.set_stack(Some(stack)); let menu_bar = gio::Menu::new(); menu_bar.append(Some("Launch new executable"), Some("app.new-task")); let settings_menu = gio::Menu::new(); settings_menu.append(Some("Display temperature in °F"), Some("app.temperature")); settings_menu.append(Some("Display graphs"), Some("app.graphs")); settings_menu.append(Some("More settings..."), Some("app.settings")); menu_bar.append_section(None, &settings_menu); let more_menu = gio::Menu::new(); more_menu.append(Some("About"), Some("app.about")); menu_bar.append_section(None, &more_menu); let menu = gio::Menu::new(); menu.append(Some("Quit"), Some("app.quit")); menu_bar.append_section(None, &menu); let menu_button = gtk::MenuButton::builder() .icon_name("open-menu-symbolic") .menu_model(&menu_bar) .build(); menu_button.add_css_class("titlebar-button"); let search_filter_button = gtk::Button::from_icon_name("edit-find-symbolic"); search_filter_button.add_css_class("titlebar-button"); let header_bar = gtk::HeaderBar::new(); header_bar.pack_end(&menu_button); header_bar.pack_end(&search_filter_button); header_bar.set_title_widget(Some(&header_buttons)); (header_bar, search_filter_button) } fn build_ui(application: >k::Application) { let window = gtk::ApplicationWindow::new(application); let stack = gtk::Stack::new(); let (header_bar, search_filter_button) = create_header_bar(&stack); window.set_titlebar(Some(&header_bar)); let mut sys = sysinfo::System::new_with_specifics(RefreshKind::everything()); let procs = Procs::new(sys.processes(), &stack); let current_pid = Rc::clone(&procs.current_pid); let info_button = procs.info_button.clone(); window.set_default_size(630, 700); sys.refresh_all(); let sys = Arc::new(Mutex::new(sys)); procs .kill_button .connect_clicked(glib::clone!(@weak current_pid, @weak sys => move |_| { let sys = sys.lock().expect("failed to lock to kill a process"); if let Some(process) = current_pid.get().and_then(|pid| sys.process(pid)) { process.kill(); } })); let settings = Settings::load(); let sys_components = sysinfo::Components::new_with_refreshed_list(); let display_tab = DisplaySysInfo::new(&sys, &sys_components, &stack, &settings); let settings = Rc::new(RefCell::new(settings)); let networks = Arc::new(Mutex::new(Networks::new_with_refreshed_list())); let network_tab = Rc::new(RefCell::new(Network::new(&stack, &networks))); display_disk::create_disk_info(&stack); let display_tab = Rc::new(RefCell::new(display_tab)); window.set_child(Some(&stack)); let process_dialogs: Rc>> = Rc::new(RefCell::new(Vec::new())); let list_store = procs.list_store.clone(); let rfs = RequiredForSettings { process_refresh_timeout: Arc::new(Mutex::new(settings.borrow().refresh_processes_rate)), network_refresh_timeout: Arc::new(Mutex::new(settings.borrow().refresh_network_rate)), system_refresh_timeout: Arc::new(Mutex::new(settings.borrow().refresh_system_rate)), sys: sys.clone(), process_dialogs: process_dialogs.clone(), list_store, display_tab, network_tab: network_tab.clone(), }; setup_timeout(&rfs); setup_network_timeout(&rfs, networks); setup_system_timeout(&rfs, &settings, sys_components); let settings_action = gio::SimpleAction::new("settings", None); settings_action.connect_activate(glib::clone!(@weak settings, @strong rfs => move |_, _| { settings::show_settings_dialog(&settings, &rfs); })); info_button.connect_clicked( glib::clone!(@weak current_pid, @weak process_dialogs, @weak sys => move |_| { if let Some(pid) = current_pid.get() { create_new_proc_diag( &process_dialogs, pid, &sys.lock().expect("failed to lock to create new proc dialog"), ); } }), ); procs .left_tree .connect_row_activated(glib::clone!(@weak sys => move |tree_view, path, _| { let model = tree_view.model().expect("couldn't get model"); let iter = model.iter(path).expect("couldn't get iter"); let pid = model.get_value(&iter, 0) .get::() .expect("Model::get failed"); create_new_proc_diag( &process_dialogs, Pid::from_u32(pid), &sys.lock().expect("failed to lock to create new proc dialog (from tree)"), ); } )); let about = gio::SimpleAction::new("about", None); about.connect_activate(glib::clone!(@weak window => move |_, _| { let p = AboutDialog::builder() .authors(vec!["Guillaume Gomez".to_owned()]) .website_label("my website") .website("https://guillaume-gomez.fr/") .comments("A process viewer GUI written with gtk-rs") .copyright("Licensed under MIT") .program_name("process-viewer") .transient_for(&window) .modal(true); let bytes = Bytes::from_static(include_bytes!( concat!(env!("CARGO_MANIFEST_DIR"), "/assets/eye.png"))); let memory_stream = MemoryInputStream::from_bytes(&bytes); let pixbuf = Pixbuf::from_stream(&memory_stream, gio::Cancellable::NONE); let p = if let Ok(pixbuf) = pixbuf { let logo = Texture::for_pixbuf(&pixbuf); p.logo(&logo) } else { p }; p.build().show(); })); let new_task = gio::SimpleAction::new("new-task", None); new_task.connect_activate(glib::clone!(@weak window => move |_, _| { let dialog = gtk::Dialog::with_buttons( Some("Launch new executable"), Some(&window), gtk::DialogFlags::MODAL, &[("Run", gtk::ResponseType::Other(0)), ("Cancel", gtk::ResponseType::Close)], ); let input = Entry::builder() .css_classes(vec!["button-with-margin".to_owned()]) .vexpand(false) .hexpand(true) .build(); // To set "run" button disabled by default. dialog.set_response_sensitive(gtk::ResponseType::Other(0), false); input.connect_changed(glib::clone!(@weak dialog => move |input| { if !input.text().is_empty() { dialog.set_response_sensitive(gtk::ResponseType::Other(0), true); } else { dialog.set_response_sensitive(gtk::ResponseType::Other(0), false); } })); input.connect_activate(glib::clone!(@weak window, @weak dialog => move |input| { run_command(input, &window, &dialog); })); dialog.connect_response(glib::clone!(@weak input, @weak window => move |dialog, response| { match response { gtk::ResponseType::Close => { dialog.close(); } gtk::ResponseType::Other(0) => { run_command(&input, &window, dialog); } _ => {} } })); dialog.content_area().append(&input); dialog.set_size_request(400, 70); dialog.show(); })); let graphs = gio::SimpleAction::new_stateful( "graphs", None, &settings.borrow().display_graph.to_variant(), ); graphs.connect_activate(glib::clone!(@weak settings => move |g, _| { let mut is_active = false; if let Some(g) = g.state() { is_active = g.get().expect("couldn't get bool"); rfs.display_tab.borrow().set_checkboxes_state(!is_active); } // We need to change the toggle state ourselves. `gio` dark magic. g.change_state(&(!is_active).to_variant()); // We update the setting and save it! settings.borrow_mut().display_graph = !is_active; settings.borrow().save(); })); let temperature = gio::SimpleAction::new_stateful( "temperature", None, &settings.borrow().display_fahrenheit.to_variant(), ); temperature.connect_activate(move |g, _| { let mut is_active = false; if let Some(g) = g.state() { is_active = g.get().expect("couldn't get graph state"); } // We need to change the toggle state ourselves. `gio` dark magic. g.change_state(&(!is_active).to_variant()); // We update the setting and save it! settings.borrow_mut().display_fahrenheit = !is_active; settings.borrow().save(); }); let quit = gio::SimpleAction::new("quit", None); quit.connect_activate(glib::clone!(@weak application => move |_,_| { application.quit(); })); application.set_accels_for_action("app.quit", &["Q"]); let finder = gio::SimpleAction::new("finder", None); // Little hack to correctly handle `ctrl+F` shortcut. finder.connect_activate(glib::clone!(@weak search_filter_button => move |_,_| { search_filter_button.emit_clicked(); })); application.set_accels_for_action("app.finder", &["F"]); application.add_action(&about); application.add_action(&graphs); application.add_action(&temperature); application.add_action(&settings_action); application.add_action(&new_task); application.add_action(&quit); application.add_action(&finder); window.set_widget_name(utils::MAIN_WINDOW_NAME); application.connect_activate(glib::clone!(@weak window => move |_| { window.present(); })); procs.search_bar.set_key_capture_widget(Some(&window)); fn revert_display(search_bar: >k::SearchBar) { if search_bar.is_search_mode() { search_bar.set_search_mode(false); } else { search_bar.set_search_mode(true); } } search_filter_button.connect_clicked(glib::clone!( @strong stack, @weak procs.search_bar as procs_search_bar, @weak network_tab, => move |_| { if let Some(name) = stack.visible_child_name() { match name.as_str() { "Processes" => revert_display(&procs_search_bar), "Networks" => revert_display(&network_tab.borrow().search_bar), _ => {} }; } })); stack.connect_visible_child_notify(move |s| { if let Some(name) = s.visible_child_name() { match name.as_str() { "Processes" => { procs.search_bar.set_key_capture_widget(Some(&window)); network_tab .borrow() .search_bar .set_key_capture_widget(gtk::Widget::NONE); search_filter_button.set_sensitive(true); return; } "Networks" => { network_tab .borrow() .search_bar .set_key_capture_widget(Some(&window)); procs.search_bar.set_key_capture_widget(gtk::Widget::NONE); search_filter_button.set_sensitive(true); return; } _ => {} } } search_filter_button.set_sensitive(false); procs.search_bar.set_key_capture_widget(gtk::Widget::NONE); network_tab .borrow() .search_bar .set_key_capture_widget(gtk::Widget::NONE); }); } fn main() { let application = gtk::Application::new(Some(APPLICATION_NAME), gio::ApplicationFlags::empty()); application.connect_startup(move |app| { let provider = gtk::CssProvider::new(); // Style needed for graph. provider.load_from_data( r#" graph_widget { color: @theme_fg_color; } .titlebar-button { background-color: @theme_bg_color; border-radius: 4px; } .titlebar-button:hover { background-color: shade(@theme_bg_color, 1.50); } .button-with-margin { margin: 6px; } "#, ); gtk::style_context_add_provider_for_display( &gdk::Display::default().expect("Could not connect to a display."), &provider, gtk::STYLE_PROVIDER_PRIORITY_APPLICATION, ); build_ui(app); }); glib::set_application_name("process-viewer"); application.run(); } process_viewer-0.5.9/src/network_dialog.rs000064400000000000000000000323461046102023000170530ustar 00000000000000use gtk::prelude::*; use gtk::{glib, EventControllerKey}; use crate::graph::GraphWidget; use crate::notebook::NoteBook; use crate::utils::{ format_number, format_number_full, get_main_window, graph_label, graph_label_units, RotateVec, }; use std::cell::{Cell, RefCell}; use std::iter; use std::rc::Rc; pub struct NetworkDialog { pub name: String, popup: gtk::Window, packets_errors_history: Rc>, in_out_history: Rc>, received_peak: Rc>, transmitted_peak: Rc>, packets_received_peak: Rc>, packets_transmitted_peak: Rc>, errors_on_received_peak: Rc>, errors_on_transmitted_peak: Rc>, to_be_removed: Rc>, list_store: gtk::ListStore, } macro_rules! update_graph { ($this:expr, $t:expr, $pos:expr, $value:expr, $total_value:expr, $peak:ident, $list_pos:expr, $formatter:ident) => {{ $t.data($pos, |d| { d.move_start(); *d.get_mut(0).expect("cannot get data 0") = $value as f32; }); let mut x = $this.$peak.borrow_mut(); if *x < $value { *x = $value; if let Some(iter) = $this.list_store.iter_nth_child(None, $list_pos - 1) { $this.list_store.set(&iter, &[(1, &$formatter($value))]); } } if let Some(iter) = $this.list_store.iter_nth_child(None, $list_pos - 2) { $this.list_store.set(&iter, &[(1, &$formatter($value))]); } if let Some(iter) = $this.list_store.iter_nth_child(None, $list_pos) { $this .list_store .set(&iter, &[(1, &$formatter($total_value))]); } }}; } impl NetworkDialog { #[allow(clippy::cognitive_complexity)] pub fn update(&self, network: &sysinfo::NetworkData) { if self.need_remove() { return; } fn formatter(value: u64) -> String { format_number_full(value, false) } let t = self.packets_errors_history.borrow_mut(); update_graph!( self, t, 0, network.packets_received(), network.total_packets_received(), packets_received_peak, 9, formatter ); update_graph!( self, t, 1, network.packets_transmitted(), network.total_packets_transmitted(), packets_transmitted_peak, 12, formatter ); update_graph!( self, t, 2, network.errors_on_received(), network.total_errors_on_received(), errors_on_received_peak, 15, formatter ); update_graph!( self, t, 3, network.errors_on_transmitted(), network.total_errors_on_transmitted(), errors_on_transmitted_peak, 18, formatter ); t.queue_draw(); let t = self.in_out_history.borrow_mut(); update_graph!( self, t, 0, network.received(), network.total_received(), received_peak, 3, format_number ); update_graph!( self, t, 1, network.transmitted(), network.total_transmitted(), transmitted_peak, 6, format_number ); t.queue_draw(); } pub fn show(&self) { self.popup.present(); } pub fn need_remove(&self) -> bool { self.to_be_removed.get() } } fn append_text_column(tree: >k::TreeView, title: &str, pos: i32, right_align: bool) { let column = gtk::TreeViewColumn::builder() .title(title) .resizable(true) .build(); let cell = gtk::CellRendererText::new(); if right_align { cell.set_xalign(1.0); } column.pack_start(&cell, true); column.add_attribute(&cell, "text", pos); if pos == 1 { cell.set_wrap_mode(gtk::pango::WrapMode::Char); column.set_expand(true); } tree.append_column(&column); } pub fn create_network_dialog( network: &sysinfo::NetworkData, interface_name: &str, ) -> NetworkDialog { let mut notebook = NoteBook::new(); let popup = gtk::Window::new(); popup.set_title(Some(&format!( "Information about network {}", interface_name ))); popup.set_transient_for(get_main_window().as_ref()); popup.set_destroy_with_parent(true); let close_button = gtk::Button::with_label("Close"); close_button.add_css_class("button-with-margin"); let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0); notebook.notebook.set_hexpand(true); notebook.notebook.set_vexpand(true); vertical_layout.append(¬ebook.notebook); vertical_layout.append(&close_button); popup.set_child(Some(&vertical_layout)); // // GRAPH TAB // let vertical_layout = gtk::Box::builder() .orientation(gtk::Orientation::Vertical) .spacing(5) .margin_top(10) .margin_bottom(10) .margin_start(5) .margin_end(5) .build(); let scroll = gtk::ScrolledWindow::new(); let in_out_history = GraphWidget::new(Some(1.), false); in_out_history.push( RotateVec::new(iter::repeat(0f32).take(61).collect()), "received", None, ); in_out_history.push( RotateVec::new(iter::repeat(0f32).take(61).collect()), "transmitted", None, ); in_out_history.set_labels_callback(Some(Box::new(graph_label_units))); let label = gtk::Label::new(None); label.set_markup("Network usage"); vertical_layout.append(&label); vertical_layout.append(&in_out_history); in_out_history.queue_draw(); // let in_out_history = connect_graph(in_out_history); let in_out_history = Rc::new(RefCell::new(in_out_history)); let packets_errors_history = GraphWidget::new(Some(1.), false); packets_errors_history.push( RotateVec::new(iter::repeat(0f32).take(61).collect()), "received packets", None, ); packets_errors_history.push( RotateVec::new(iter::repeat(0f32).take(61).collect()), "transmitted packets", None, ); packets_errors_history.push( RotateVec::new(iter::repeat(0f32).take(61).collect()), "errors on received", None, ); packets_errors_history.push( RotateVec::new(iter::repeat(0f32).take(61).collect()), "errors on transmitted", None, ); packets_errors_history.set_labels_callback(Some(Box::new(graph_label))); let label = gtk::Label::new(None); label.set_markup("Extra data"); vertical_layout.append(&label); vertical_layout.append(&packets_errors_history); packets_errors_history.queue_draw(); // let packets_errors_history = connect_graph(packets_errors_history); let packets_errors_history = Rc::new(RefCell::new(packets_errors_history)); scroll.set_child(Some(&vertical_layout)); scroll.connect_show( glib::clone!(@weak packets_errors_history, @weak in_out_history => move |_| { packets_errors_history.borrow().show(); in_out_history.borrow().show(); }), ); notebook.create_tab("Graphics", &scroll); // // NETWORK INFO TAB // let list_store = gtk::ListStore::new(&[glib::Type::STRING, glib::Type::STRING]); let tree = gtk::TreeView::builder() .headers_visible(true) .model(&list_store) .build(); append_text_column(&tree, "property", 0, false); append_text_column(&tree, "value", 1, true); list_store.insert_with_values( None, &[(0, &"MAC address"), (1, &network.mac_address().to_string())], ); list_store.insert_with_values( None, &[(0, &"received"), (1, &format_number(network.received()))], ); list_store.insert_with_values( None, &[ (0, &"received peak"), (1, &format_number(network.received())), ], ); list_store.insert_with_values( None, &[ (0, &"total received"), (1, &format_number(network.total_received())), ], ); list_store.insert_with_values( None, &[ (0, &"transmitted"), (1, &format_number(network.transmitted())), ], ); list_store.insert_with_values( None, &[ (0, &"transmitted peak"), (1, &format_number(network.transmitted())), ], ); list_store.insert_with_values( None, &[ (0, &"total transmitted"), (1, &format_number(network.total_transmitted())), ], ); list_store.insert_with_values( None, &[ (0, &"packets received"), (1, &format_number_full(network.packets_received(), false)), ], ); list_store.insert_with_values( None, &[ (0, &"packets received peak"), (1, &format_number(network.packets_received())), ], ); list_store.insert_with_values( None, &[ (0, &"total packets received"), ( 1, &format_number_full(network.total_packets_received(), false), ), ], ); list_store.insert_with_values( None, &[ (0, &"packets transmitted"), (1, &format_number_full(network.packets_transmitted(), false)), ], ); list_store.insert_with_values( None, &[ (0, &"packets transmitted peak"), (1, &format_number(network.packets_transmitted())), ], ); list_store.insert_with_values( None, &[ (0, &"total packets transmitted"), ( 1, &format_number_full(network.total_packets_transmitted(), false), ), ], ); list_store.insert_with_values( None, &[ (0, &"errors on received"), (1, &format_number_full(network.errors_on_received(), false)), ], ); list_store.insert_with_values( None, &[ (0, &"errors on received peak"), (1, &format_number(network.errors_on_received()).as_str()), ], ); list_store.insert_with_values( None, &[ (0, &"total errors on received"), ( 1, &format_number_full(network.total_errors_on_received(), false), ), ], ); list_store.insert_with_values( None, &[ (0, &"errors on transmitted"), ( 1, &format_number_full(network.errors_on_transmitted(), false), ), ], ); list_store.insert_with_values( None, &[ (0, &"errors on transmitted peak"), (1, &format_number(network.errors_on_transmitted())), ], ); list_store.insert_with_values( None, &[ (0, &"total errors on transmitted"), ( 1, &format_number_full(network.total_errors_on_transmitted(), false), ), ], ); notebook.create_tab("Information", &tree); popup.set_size_request(700, 540); let to_be_removed = Rc::new(Cell::new(false)); popup.connect_destroy(glib::clone!(@weak to_be_removed => move |_| { to_be_removed.set(true); })); close_button.connect_clicked(glib::clone!(@weak popup, @weak to_be_removed => move |_| { popup.close(); })); popup.connect_close_request( glib::clone!(@weak to_be_removed => @default-return glib::Propagation::Proceed, move |_| { to_be_removed.set(true); glib::Propagation::Proceed }), ); let event_controller = EventControllerKey::new(); event_controller.connect_key_pressed(glib::clone!( @weak popup, @weak to_be_removed => @default-return glib::Propagation::Proceed, move |_, key, _, _modifier| { if key == gtk::gdk::Key::Escape { popup.close(); to_be_removed.set(true); } glib::Propagation::Proceed } )); popup.add_controller(event_controller); popup.set_resizable(true); popup.show(); let adjust = scroll.vadjustment(); adjust.set_value(0.); scroll.set_vadjustment(Some(&adjust)); NetworkDialog { name: interface_name.to_owned(), popup, packets_errors_history, in_out_history, received_peak: Rc::new(RefCell::new(network.received())), transmitted_peak: Rc::new(RefCell::new(network.transmitted())), packets_received_peak: Rc::new(RefCell::new(network.packets_received())), packets_transmitted_peak: Rc::new(RefCell::new(network.packets_transmitted())), errors_on_received_peak: Rc::new(RefCell::new(network.errors_on_received())), errors_on_transmitted_peak: Rc::new(RefCell::new(network.errors_on_transmitted())), to_be_removed, list_store, } } process_viewer-0.5.9/src/notebook.rs000064400000000000000000000013111046102023000156470ustar 00000000000000use gtk::prelude::*; use gtk::{Box, Label, Notebook, Orientation, Widget}; pub struct NoteBook { pub notebook: Notebook, pub tabs: Vec, } impl NoteBook { pub fn new() -> NoteBook { NoteBook { notebook: Notebook::new(), tabs: Vec::new(), } } pub fn create_tab>(&mut self, title: &str, widget: &T) -> Option { let label = Label::new(Some(title)); let tab = Box::new(Orientation::Horizontal, 0); tab.set_hexpand(true); tab.set_vexpand(true); tab.append(&label); let index = self.notebook.append_page(widget, Some(&tab)); self.tabs.push(tab); Some(index) } } process_viewer-0.5.9/src/process_dialog.rs000064400000000000000000000336421046102023000170400ustar 00000000000000use gtk::prelude::*; use gtk::{glib, pango, EventControllerKey}; use sysinfo::Pid; use std::cell::{Cell, RefCell}; use std::fmt; use std::iter; use std::rc::Rc; use crate::graph::GraphWidget; use crate::notebook::NoteBook; use crate::utils::{format_number, get_main_window, graph_label_units, RotateVec}; #[allow(dead_code)] pub struct ProcDialog { working_directory: gtk::Label, memory_usage: gtk::Label, disk_usage: gtk::Label, cpu_usage: gtk::Label, run_time: gtk::Label, pub popup: gtk::Window, pub pid: Pid, notebook: NoteBook, ram_usage_history: Rc>, cpu_usage_history: Rc>, disk_usage_history: Rc>, memory_peak: RefCell, memory_peak_label: gtk::Label, disk_peak: RefCell, disk_peak_label: gtk::Label, pub is_dead: bool, pub to_be_removed: Rc>, } impl fmt::Debug for ProcDialog { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ProcDialog {{ pid: {} }}", self.pid) } } impl ProcDialog { pub fn update(&self, process: &sysinfo::Process) { if self.is_dead { return; } self.working_directory.set_text( &process .cwd() .map(|path| path.display().to_string()) .unwrap_or_default(), ); let memory = process.memory(); let memory_s = format_number(memory); self.memory_usage.set_text(&memory_s); if memory > *self.memory_peak.borrow() { *self.memory_peak.borrow_mut() = memory; self.memory_peak_label.set_text(&memory_s); } let disk_usage = process.disk_usage(); let disk_usage = disk_usage.written_bytes + disk_usage.read_bytes; let disk_usage_s = format_number(disk_usage); self.disk_usage.set_text(&disk_usage_s); if disk_usage > *self.disk_peak.borrow() { *self.disk_peak.borrow_mut() = disk_usage; self.disk_peak_label.set_text(&disk_usage_s); } self.cpu_usage .set_text(&format!("{:.1}%", process.cpu_usage())); self.run_time.set_text(&format_time(process.run_time())); let t = self.ram_usage_history.borrow_mut(); t.data(0, |d| { d.move_start(); *d.get_mut(0).expect("cannot get data 0") = memory as f32; }); t.queue_draw(); let t = self.cpu_usage_history.borrow_mut(); t.data(0, |d| { d.move_start(); *d.get_mut(0).expect("cannot get data 0") = process.cpu_usage(); }); t.queue_draw(); let t = self.disk_usage_history.borrow_mut(); t.data(0, |d| { d.move_start(); *d.get_mut(0).expect("cannot get data 0") = disk_usage as f32; }); t.queue_draw(); } pub fn need_remove(&self) -> bool { self.to_be_removed.get() } pub fn set_dead(&mut self) { if self.is_dead { return; } self.is_dead = true; self.memory_usage.set_text("0"); self.disk_usage.set_text("0"); self.cpu_usage.set_text("0%"); let time = self.run_time.text(); let s = format!("Ran for {}", if time.is_empty() { "0s" } else { &time },); self.run_time.set_text(&s); } } fn format_time(t: u64) -> String { format!( "{}{}{}{}s", { let days = t / 86_400; if days > 0 { format!("{}d ", days) } else { "".to_owned() } }, { let hours = t / 3_600 % 24; if hours > 0 { format!("{}h ", hours) } else { "".to_owned() } }, { let minutes = t / 60 % 60; if minutes > 0 { format!("{}m ", minutes) } else { "".to_owned() } }, t % 60 ) } fn create_and_add_new_label(scroll: >k::Box, title: &str, text: &str) -> gtk::Label { let horizontal_layout = gtk::Box::new(gtk::Orientation::Horizontal, 0); horizontal_layout.set_margin_top(5); horizontal_layout.set_margin_bottom(5); horizontal_layout.set_margin_end(5); horizontal_layout.set_margin_start(5); let label = gtk::Label::new(None); label.set_justify(gtk::Justification::Left); label.set_markup(&format!("{}: ", title)); let text = gtk::Label::new(Some(text)); text.set_selectable(true); text.set_justify(gtk::Justification::Left); text.set_wrap(true); text.set_wrap_mode(pango::WrapMode::Char); horizontal_layout.append(&label); horizontal_layout.append(&text); scroll.append(&horizontal_layout); text } fn append_text_column(tree: >k::TreeView, pos: i32) { let column = gtk::TreeViewColumn::new(); let cell = gtk::CellRendererText::new(); column.pack_start(&cell, true); column.add_attribute(&cell, "text", pos); if pos == 1 { cell.set_wrap_width(247); cell.set_wrap_mode(pango::WrapMode::Char); column.set_expand(true); } tree.append_column(&column); } pub fn create_process_dialog(process: &sysinfo::Process, total_memory: u64) -> ProcDialog { let mut notebook = NoteBook::new(); let popup = gtk::Window::new(); popup.set_title(Some(&format!("Information about {}", process.name()))); popup.set_transient_for(get_main_window().as_ref()); popup.set_destroy_with_parent(true); // // PROCESS INFO TAB // let scroll = gtk::ScrolledWindow::new(); let close_button = gtk::Button::with_label("Close"); close_button.add_css_class("button-with-margin"); let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0); scroll.set_policy(gtk::PolicyType::Never, gtk::PolicyType::Automatic); let running_since = process.run_time(); let labels = gtk::Box::new(gtk::Orientation::Vertical, 0); create_and_add_new_label(&labels, "name", process.name()); create_and_add_new_label(&labels, "pid", &process.pid().to_string()); let memory_peak = process.memory(); let memory_usage = create_and_add_new_label(&labels, "memory usage", &format_number(memory_peak)); let memory_peak_label = create_and_add_new_label(&labels, "memory usage peak", &format_number(memory_peak)); let disk_peak = process.disk_usage(); let disk_peak = disk_peak.written_bytes + disk_peak.read_bytes; let s; #[cfg(not(any(windows, target_os = "freebsd")))] { s = "disk I/O usage"; } #[cfg(any(windows, target_os = "freebsd"))] { s = "I/O usage"; } let disk_usage = create_and_add_new_label(&labels, s, &format_number(disk_peak)); let disk_peak_label = create_and_add_new_label(&labels, &format!("{} peak", s), &format_number(disk_peak)); let cpu_usage = create_and_add_new_label( &labels, "cpu usage", &format!("{:.1}%", process.cpu_usage()), ); let run_time = create_and_add_new_label(&labels, "Running since", &format_time(running_since)); create_and_add_new_label( &labels, "command", &format!( "[{}]", process .cmd() .iter() .map(|x| format!("\"{}\"", x)) .collect::>() .join(", ") ), ); create_and_add_new_label( &labels, "executable path", &process .exe() .map(|path| path.display().to_string()) .unwrap_or_default(), ); let working_directory = create_and_add_new_label( &labels, "current working directory", &process .cwd() .map(|path| path.display().to_string()) .unwrap_or_default(), ); create_and_add_new_label( &labels, "root directory", &process .root() .map(|path| path.display().to_string()) .unwrap_or_default(), ); let env_tree = gtk::TreeView::new(); let list_store = gtk::ListStore::new(&[glib::Type::STRING, glib::Type::STRING]); env_tree.set_headers_visible(false); env_tree.set_model(Some(&list_store)); append_text_column(&env_tree, 0); append_text_column(&env_tree, 1); for env in process.environ() { let mut parts = env.splitn(2, '='); let name = match parts.next() { Some(n) => n, None => continue, }; let value = parts.next().unwrap_or(""); list_store.insert_with_values(None, &[(0, &name), (1, &value)]); } let components = gtk::Box::new(gtk::Orientation::Vertical, 0); components.append(&labels); if !process.environ().is_empty() { let label = gtk::Label::new(None); label.set_markup("Environment variables"); components.append(&label); components.append(&env_tree); } scroll.set_child(Some(&components)); scroll.set_hexpand(true); scroll.set_vexpand(true); vertical_layout.append(&scroll); vertical_layout.append(&close_button); notebook.create_tab("Information", &vertical_layout); // // GRAPH TAB // let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0); vertical_layout.set_spacing(5); vertical_layout.set_margin_top(10); vertical_layout.set_margin_bottom(10); vertical_layout.set_margin_start(5); vertical_layout.set_margin_end(5); let scroll = gtk::ScrolledWindow::new(); let cpu_usage_history = GraphWidget::new(Some(100.), false); // In case a process uses more than 100% cpu_usage_history.set_display_labels(false); cpu_usage_history.set_minimum(Some(100.)); let ram_usage_history = GraphWidget::new(Some(total_memory as f32), false); ram_usage_history.set_display_labels(false); ram_usage_history.set_overhead(Some(20.)); let disk_usage_history = GraphWidget::new(Some(0f32), false); disk_usage_history.set_display_labels(false); disk_usage_history.set_overhead(Some(20.)); cpu_usage_history.push( RotateVec::new(iter::repeat(0f32).take(61).collect()), "", None, ); cpu_usage_history.set_labels_callback(Some(Box::new(|v| { if v > 100. { let nb = v.ceil() as u64; [ nb.to_string(), (nb / 2).to_string(), "0".to_string(), "%".to_string(), ] } else { [ "100".to_string(), "50".to_string(), "0".to_string(), "%".to_string(), ] } }))); vertical_layout.append(>k::Label::new(Some("Process usage"))); vertical_layout.append(&cpu_usage_history); cpu_usage_history.queue_draw(); // let cpu_usage_history = connect_graph(cpu_usage_history); let cpu_usage_history = Rc::new(RefCell::new(cpu_usage_history)); ram_usage_history.push( RotateVec::new(iter::repeat(0f32).take(61).collect()), "", None, ); disk_usage_history.push( RotateVec::new(iter::repeat(0f32).take(61).collect()), "", None, ); ram_usage_history.set_labels_callback(Some(Box::new(graph_label_units))); disk_usage_history.set_labels_callback(Some(Box::new(graph_label_units))); vertical_layout.append(>k::Label::new(Some("Memory usage"))); vertical_layout.append(&ram_usage_history); ram_usage_history.queue_draw(); // let ram_usage_history = connect_graph(ram_usage_history); let ram_usage_history = Rc::new(RefCell::new(ram_usage_history)); #[cfg(not(windows))] { vertical_layout.append(>k::Label::new(Some("Disk I/O usage"))); } #[cfg(windows)] { vertical_layout.append(>k::Label::new(Some("I/O usage"))); } vertical_layout.append(&disk_usage_history); disk_usage_history.queue_draw(); let disk_usage_history = Rc::new(RefCell::new(disk_usage_history)); scroll.set_child(Some(&vertical_layout)); scroll.connect_show( glib::clone!(@weak ram_usage_history, @weak cpu_usage_history, @weak disk_usage_history => move |_| { ram_usage_history.borrow().show(); cpu_usage_history.borrow().show(); disk_usage_history.borrow().show(); }), ); notebook.create_tab("Resources usage", &scroll); popup.set_child(Some(¬ebook.notebook)); popup.set_size_request(500, 600); close_button.connect_clicked(glib::clone!(@weak popup => move |_| { popup.close(); })); let to_be_removed = Rc::new(Cell::new(false)); popup.connect_destroy(glib::clone!(@weak to_be_removed => move |_| { to_be_removed.set(true); })); popup.connect_close_request( glib::clone!(@weak to_be_removed => @default-return glib::Propagation::Proceed, move |_| { to_be_removed.set(true); glib::Propagation::Proceed }), ); let event_controller = EventControllerKey::new(); event_controller.connect_key_pressed( glib::clone!(@weak popup, @weak to_be_removed => @default-return glib::Propagation::Proceed, move |_, key, _, _modifier| { if key == gtk::gdk::Key::Escape { popup.close(); to_be_removed.set(true); } glib::Propagation::Proceed }), ); popup.add_controller(event_controller); popup.set_resizable(true); popup.show(); let adjust = scroll.vadjustment(); adjust.set_value(0.); scroll.set_vadjustment(Some(&adjust)); ProcDialog { working_directory, memory_usage, disk_usage, cpu_usage, run_time, popup, pid: process.pid(), notebook, ram_usage_history, cpu_usage_history, disk_usage_history, memory_peak: RefCell::new(memory_peak), memory_peak_label, disk_peak: RefCell::new(disk_peak), disk_peak_label, is_dead: false, to_be_removed, } } process_viewer-0.5.9/src/settings.rs000064400000000000000000000162461046102023000157040ustar 00000000000000// // Process viewer // // Copyright (c) 2019 Guillaume Gomez // use gtk::glib; use gtk::prelude::*; use serde_derive::{Deserialize, Serialize}; use std::cell::RefCell; use std::fs::{create_dir_all, File}; use std::io::Read; use std::path::{Path, PathBuf}; use std::rc::Rc; use crate::utils::{get_app, get_main_window}; use crate::RequiredForSettings; use crate::APPLICATION_NAME; #[derive(Deserialize, Serialize, Debug, Clone)] pub struct Settings { pub display_fahrenheit: bool, pub display_graph: bool, // Timer length in milliseconds (500 minimum!). pub refresh_processes_rate: u32, // Timer length in milliseconds (500 minimum!). pub refresh_system_rate: u32, // Timer length in milliseconds (500 minimum!). pub refresh_network_rate: u32, } impl Default for Settings { fn default() -> Settings { Settings { display_fahrenheit: false, display_graph: false, refresh_processes_rate: 1500, refresh_system_rate: 2000, refresh_network_rate: 1500, } } } impl Settings { fn load_from_file(p: &Path) -> Result { let mut input = String::new(); let mut file = File::open(p).map_err(|e| format!("Error while opening '{}': {}", p.display(), e))?; file.read_to_string(&mut input) .map_err(|e| format!("Error while opening '{}': {}", p.display(), e))?; toml::from_str(&input).map_err(|e| format!("Error while opening '{}': {}", p.display(), e)) } pub fn load() -> Settings { let s = Self::get_settings_file_path(); if s.exists() && s.is_file() { match Self::load_from_file(&s) { Ok(settings) => settings, Err(e) => { show_error_dialog(false, &e); Settings::default() } } } else { Settings::default() } } pub fn get_settings_file_path() -> PathBuf { let mut path = glib::user_config_dir(); path.push(APPLICATION_NAME); path.push("settings.toml"); path } pub fn save(&self) { let s = Self::get_settings_file_path(); if !s.exists() { if let Some(parent_dir) = s.parent() { if !parent_dir.exists() { if let Err(e) = create_dir_all(parent_dir) { show_error_dialog( false, format!( "Error while trying to build settings snapshot_directory '{}': {}", parent_dir.display(), e ) .as_str(), ); return; } } } } match toml::to_string_pretty(&self) { Ok(output) => { if let Err(e) = std::fs::write(&s, output) { show_error_dialog( false, format!("Error while trying to save file: {}", e).as_str(), ); } } Err(e) => { show_error_dialog( false, format!("Error while trying to save file: {}", e).as_str(), ); } } } } fn show_error_dialog(fatal: bool, text: &str) { let dialog = gtk::MessageDialog::new( get_main_window().as_ref(), gtk::DialogFlags::MODAL, gtk::MessageType::Error, gtk::ButtonsType::Ok, text, ); dialog.connect_response(move |dialog, _| { dialog.close(); if fatal { get_app().quit(); } }); dialog.set_resizable(false); dialog.show(); } pub fn build_spin(label: &str, grid: >k::Grid, top: i32, refresh: u32) -> gtk::SpinButton { // Refresh rate. let refresh_label = gtk::Label::builder() .label(label) .halign(gtk::Align::Start) .hexpand(true) .build(); // We allow 0.5 to 5 seconds, in 0.1 second steps. let refresh_entry = gtk::SpinButton::with_range(0.5, 5., 0.1); refresh_entry.set_value(f64::from(refresh) / 1_000.); grid.attach(&refresh_label, 0, top, 1, 1); grid.attach(&refresh_entry, 1, top, 3, 1); refresh_entry } pub fn show_settings_dialog(settings: &Rc>, rfs: &RequiredForSettings) { let bsettings = &*settings.borrow(); // Create an empty dialog with close button. let dialog = gtk::Dialog::with_buttons( Some("Process Viewer settings"), get_main_window().as_ref(), gtk::DialogFlags::MODAL, &[("Close", gtk::ResponseType::Close)], ); // All the UI widgets are going to be stored in a grid. let grid = gtk::Grid::builder() .column_spacing(4) .row_spacing(4) .margin_bottom(12) .build(); let refresh_procs = build_spin( "Processes refresh rate (in seconds)", &grid, 0, bsettings.refresh_processes_rate, ); let refresh_network = build_spin( "System network refresh rate (in seconds)", &grid, 1, bsettings.refresh_network_rate, ); let refresh_sys = build_spin( "System information refresh rate (in seconds)", &grid, 2, bsettings.refresh_system_rate, ); // Put the grid into the dialog's content area. let content_area = dialog.content_area(); content_area.append(&grid); // content_area.set_border_width(10); // Finally connect to all kinds of change notification signals for the different UI widgets. // Whenever something is changing we directly save the configuration file with the new values. refresh_procs.connect_value_changed(glib::clone!( @weak settings, @weak rfs.process_refresh_timeout as process_refresh_timeout => move |entry| { let mut settings = settings.borrow_mut(); settings.refresh_processes_rate = (entry.value() * 1_000.) as _; *process_refresh_timeout.lock().expect("failed to lock process_refresh_timeout") = settings.refresh_processes_rate; settings.save(); })); refresh_network.connect_value_changed(glib::clone!(@weak settings, @weak rfs.network_refresh_timeout as network_refresh_timeout => move |entry| { let mut settings = settings.borrow_mut(); settings.refresh_network_rate = (entry.value() * 1_000.) as _; *network_refresh_timeout.lock().expect("failed to lock network_refresh_timeout") = settings.refresh_network_rate; settings.save(); })); refresh_sys.connect_value_changed(glib::clone!(@weak settings, @weak rfs.system_refresh_timeout as system_refresh_timeout => move |entry| { let mut settings = settings.borrow_mut(); settings.refresh_system_rate = (entry.value() * 1_000.) as _; *system_refresh_timeout.lock().expect("failed to lock system_refresh_timeout") = settings.refresh_system_rate; settings.save(); })); dialog.connect_response(move |dialog, _| { dialog.close(); }); dialog.set_resizable(false); dialog.show(); } process_viewer-0.5.9/src/utils.rs000064400000000000000000000074071046102023000152030ustar 00000000000000use gtk::gio; use gtk::prelude::*; use std::ops::Index; pub const MAIN_WINDOW_NAME: &str = "main-window"; #[derive(Debug)] pub struct RotateVec { data: Vec, start: usize, } impl RotateVec { pub fn new(d: Vec) -> RotateVec { RotateVec { data: d, start: 0 } } pub fn len(&self) -> usize { self.data.len() } pub fn is_empty(&self) -> bool { self.data.is_empty() } pub fn move_start(&mut self) { if self.start > 0 { self.start -= 1; } else { self.start = self.data.len() - 1; } } pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { let pos = self.get_real_pos(index); self.data.get_mut(pos) } fn get_real_pos(&self, index: usize) -> usize { if self.start + index >= self.data.len() { self.start + index - self.data.len() } else { index + self.start } } } pub fn format_number(nb: u64) -> String { format_number_full(nb, true) } pub fn format_number_full(nb: u64, use_unit: bool) -> String { if nb < 1_000 { format!("{}{}", nb, if use_unit { " B" } else { "" }) } else if nb < 1_000_000 { format!( "{}.{}{}", nb / 1_000, nb / 100 % 10, if use_unit { " KB" } else { "" } ) } else if nb < 1_000_000_000 { format!( "{}.{}{}", nb / 1_000_000, nb / 100_000 % 10, if use_unit { " MB" } else { "" } ) } else if nb < 1_000_000_000_000 { format!( "{}.{}{}", nb / 1_000_000_000, nb / 100_000_000 % 10, if use_unit { " GB" } else { "" } ) } else { format!( "{}.{}{}", nb / 1_000_000_000_000, nb / 100_000_000_000 % 10, if use_unit { " TB" } else { "" } ) } } pub fn graph_label_units(v: f32) -> [String; 4] { graph_label_units_full(v, true) } pub fn graph_label(v: f32) -> [String; 4] { graph_label_units_full(v, false) } pub fn graph_label_units_full(v: f32, use_unit: bool) -> [String; 4] { if v < 1_000. { [ v.to_string(), format!("{}", v / 2.), "0".to_owned(), if use_unit { "B" } else { "" }.to_owned(), ] } else if v < 1_000_000. { [ format!("{:.1}", v / 1_000f32), format!("{:.1}", v / 2_000f32), "0".to_owned(), if use_unit { "KB" } else { "K" }.to_owned(), ] } else if v < 1_000_000_000. { [ format!("{:.1}", v / 1_000_000f32), format!("{:.1}", v / 2_000_000f32), "0".to_owned(), if use_unit { "MB" } else { "M" }.to_owned(), ] } else if v < 1_000_000_000_000. { [ format!("{:.1}", v / 1_000_000_000f32), format!("{:.1}", v / 2_000_000_000f32), "0".to_owned(), if use_unit { "GB" } else { "G" }.to_owned(), ] } else { [ format!("{:.1}", v / 1_000_000_000_000f32), format!("{:.1}", v / 2_000_000_000_000f32), "0".to_owned(), if use_unit { "TB" } else { "T" }.to_owned(), ] } } impl Index for RotateVec { type Output = T; fn index(&self, index: usize) -> &T { &self.data[self.get_real_pos(index)] } } pub fn get_app() -> gtk::Application { gio::Application::default() .and_downcast::() .expect("Default application has wrong type") } pub fn get_main_window() -> Option { get_app() .windows() .into_iter() .find(|window| window.widget_name() == MAIN_WINDOW_NAME) }