diff --git a/Cargo.lock b/Cargo.lock index 0b01319..57622fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -770,6 +770,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "float_next_after" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" + [[package]] name = "foldhash" version = "0.1.5" @@ -1166,13 +1172,6 @@ dependencies = [ "thiserror 2.0.19", ] -[[package]] -name = "iced-template" -version = "0.1.0" -dependencies = [ - "iced", -] - [[package]] name = "iced_core" version = "0.14.0" @@ -1229,6 +1228,7 @@ dependencies = [ "iced_core", "iced_futures", "log", + "lyon_path", "raw-window-handle", "rustc-hash 2.1.3", "thiserror 2.0.19", @@ -1303,6 +1303,7 @@ dependencies = [ "iced_debug", "iced_graphics", "log", + "lyon", "rustc-hash 2.1.3", "thiserror 2.0.19", "wgpu", @@ -1351,6 +1352,14 @@ dependencies = [ "hashbrown 0.17.1", ] +[[package]] +name = "itop" +version = "0.1.0" +dependencies = [ + "iced", + "sysinfo", +] + [[package]] name = "jni" version = "0.22.4" @@ -1545,6 +1554,58 @@ version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" +[[package]] +name = "lyon" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0578bdecb7d6d88987b8b2b1e3a4e2f81df9d0ece1078623324a567904e7b7" +dependencies = [ + "lyon_algorithms", + "lyon_tessellation", +] + +[[package]] +name = "lyon_algorithms" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8575c0d003ae459399623c4def180c63b77f343b1a7fee64f249b349e7699a31" +dependencies = [ + "lyon_path", + "num-traits", +] + +[[package]] +name = "lyon_geom" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4336502e29e32af93cf2dad2214ed6003c17ceb5bd499df77b1de663b9042b92" +dependencies = [ + "arrayvec", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c463f9c428b7fc5ec885dcd39ce4aa61e29111d0e33483f6f98c74e89d8621e" +dependencies = [ + "lyon_geom", + "num-traits", +] + +[[package]] +name = "lyon_tessellation" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e43b7e44161571868f5c931d12583592c223c5583eef86b08aa02b7048a3552" +dependencies = [ + "float_next_after", + "lyon_path", + "num-traits", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -1674,6 +1735,15 @@ dependencies = [ "jni-sys 0.3.1", ] +[[package]] +name = "ntapi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" +dependencies = [ + "winapi", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1950,6 +2020,16 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "objc2-io-surface" version = "0.3.2" @@ -1985,6 +2065,17 @@ dependencies = [ "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-open-directory" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb82bed227edf5201dfedf072bba4015a33d3d4a98519837295a90f0a23f676d" +dependencies = [ + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-quartz-core" version = "0.2.2" @@ -2758,6 +2849,21 @@ dependencies = [ "libc", ] +[[package]] +name = "sysinfo" +version = "0.39.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2071df9448915b71c4fe6d25deaf1c22f12bd234f01540b77312bb8e41361e6" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "objc2-open-directory", + "windows 0.62.2", +] + [[package]] name = "tempfile" version = "3.27.0" @@ -3402,6 +3508,22 @@ dependencies = [ "web-sys", ] +[[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-util" version = "0.1.11" @@ -3411,6 +3533,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[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 = "window_clipboard" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index cdc8bd8..6fcaa0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,12 @@ [package] -name = "iced-template" +name = "itop" version = "0.1.0" edition = "2024" license = "AGPL-3.0-or-later" [dependencies] +sysinfo = "0.39.6" [dependencies.iced] version = "0.14.0" +features = [ "canvas" ] diff --git a/src/app/cpu_graph.rs b/src/app/cpu_graph.rs new file mode 100644 index 0000000..38c76ba --- /dev/null +++ b/src/app/cpu_graph.rs @@ -0,0 +1,49 @@ +use std::collections::VecDeque; + +use iced::{Color, Point, Renderer, Theme, mouse, widget::canvas}; + +use crate::app; + +const CYAN: Color = Color::from_rgb8(0, 255, 255); + +#[derive(Default)] +pub struct CpuGraph { + pub cache: canvas::Cache, + timeline: VecDeque, +} + +impl CpuGraph { + pub fn update(&mut self, usage: f32) { + self.timeline.push_back(usage); + if self.timeline.len() > 1000 { + self.timeline.pop_front(); + } + } +} + +impl canvas::Program for CpuGraph { + type State = (); + fn draw( + &self, + _state: &Self::State, + renderer: &Renderer, + _theme: &Theme, + bounds: iced::Rectangle, + _cursor: mouse::Cursor, + ) -> Vec> { + let geometry = self.cache.draw_with_bounds(renderer, bounds, |frame| { + frame.stroke( + &canvas::Path::new(|builder| { + for (index, value) in self.timeline.iter().enumerate() { + let x = index as f32 / self.timeline.len() as f32 * bounds.width; + let y = bounds.height - value / 100. * bounds.height; + builder.line_to(Point { x, y }) + } + }), + canvas::Stroke::default().with_color(CYAN), + ); + }); + + vec![geometry] + } +} diff --git a/src/app/mod.rs b/src/app/mod.rs index 7b9c9ca..ee59a06 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1,23 +1,68 @@ -use iced::{Color, Element, Renderer, Task, Theme, application, theme, widget}; +use std::time::Instant; -enum Message {} +use iced::{ + Color, Element, Length, Renderer, Subscription, Task, Theme, application, theme, widget, window, +}; -struct State {} +use crate::app::cpu_graph::CpuGraph; + +mod cpu_graph; + +enum Message { + Frame(Instant), +} + +struct State { + sys: sysinfo::System, + refreshes: sysinfo::RefreshKind, + + last_refresh: Instant, + + cpu_graph: CpuGraph, +} impl State { fn new() -> Self { - Self {} + Self { + sys: sysinfo::System::new(), + refreshes: sysinfo::RefreshKind::nothing() + .with_cpu(sysinfo::CpuRefreshKind::nothing().with_cpu_usage()), + + cpu_graph: Default::default(), + last_refresh: Instant::now(), + } } fn update(&mut self, message: Message) -> Task { + match message { + Message::Frame(instant) => { + if instant.duration_since(self.last_refresh) >= sysinfo::MINIMUM_CPU_UPDATE_INTERVAL + { + self.sys.refresh_specifics(self.refreshes); + self.cpu_graph.update(self.sys.global_cpu_usage()); + self.cpu_graph.cache.clear(); + + self.last_refresh = instant; + } + } + } + Task::none() } fn view(&self) -> Element<'_, Message, Theme, Renderer> { - widget::text("hiiiiiii").into() + widget::Canvas::new(&self.cpu_graph) + .width(Length::Fill) + .height(Length::Fill) + .into() + } + + fn subscription(&self) -> Subscription { + window::frames().map(Message::Frame) } } pub fn run() -> Result<(), impl std::error::Error> { application(State::new, State::update, State::view) + .subscription(State::subscription) .theme(theme::Theme::custom( "high-contrast-dark", theme::Palette {