Skip to main content

ferrix_app/
pages.rs

1/* pages.rs
2 *
3 * Copyright 2025-2026 Michail Krasnov <mskrasnov07@ya.ru>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21//! Pages with information about hardware and software
22
23use iced::{
24    Alignment::{self, Center},
25    Element,
26    widget::{center, column, container, row, rule, svg::Handle, text},
27};
28
29use crate::{
30    Message,
31    ferrix::Ferrix,
32    fl,
33    icons::ERROR_ICON,
34    widgets::{header_text, link_button},
35};
36
37mod battery;
38mod cpu;
39mod cpu_freq;
40mod dashboard;
41mod distro;
42mod dmi;
43mod drm;
44mod env;
45mod export;
46mod groups;
47mod kernel;
48mod net;
49mod ram;
50mod settings;
51mod soft;
52mod storage;
53mod sysmon;
54mod system;
55mod systemd;
56mod users;
57mod vulnerabilities;
58
59pub use sysmon::*;
60
61#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
62pub enum Page {
63    /************************************
64     *       Hardware & dashboard       *
65     ************************************/
66    #[default]
67    Dashboard,
68    Processors,
69    CPUFrequency,
70    CPUVulnerabilities,
71    SystemMonitor,
72    Memory,
73    FileSystems,
74    Network,
75    DMI,
76    Battery,
77    Screen,
78
79    /************************************
80     *          Administration          *
81     ************************************/
82    Distro,
83    SystemMisc,
84    Users,
85    Groups,
86    SystemManager,
87    Software,
88    Environment,
89    Sensors,
90
91    /************************************
92     *               Kernel             *
93     ************************************/
94    Kernel,
95    KModules,
96    Development,
97
98    /************************************
99     *              Service             *
100     ************************************/
101    Settings,
102    About,
103    Export,
104    Todo,
105}
106
107impl From<&str> for Page {
108    fn from(value: &str) -> Self {
109        match value {
110            "dash" | "dashboard" => Self::Dashboard,
111            "sysmon" | "monitor" | "system" | "system-monitor" => Self::SystemMonitor,
112            "proc" | "cpu" | "processors" => Self::Processors,
113            "cpu-frequency" | "cpufreq" => Self::CPUFrequency,
114            "cpu-vuln" | "vulnerabilities" => Self::CPUVulnerabilities,
115            "memory" | "mem" | "ram" => Self::Memory,
116            "fs" | "storage" => Self::FileSystems,
117            "net" => Self::Network,
118            "dmi" => Self::DMI,
119            "battery" | "bat" => Self::Battery,
120            "edid" | "screen" => Self::Screen,
121            "distro" => Self::Distro,
122            "users" => Self::Users,
123            "groups" => Self::Groups,
124            "misc" => Self::SystemMisc,
125            "init" | "sysd" | "systemd" => Self::SystemManager,
126            "software" | "soft" | "pkg" | "pkgs" => Self::Software,
127            "env" => Self::Environment,
128            "sensors" => Self::Sensors,
129            "kernel" | "linux" => Self::Kernel,
130            "kmods" | "mod" | "modules" => Self::KModules,
131            "dev" => Self::Development,
132            "settings" => Self::Settings,
133            "about" | "version" | "--version" | "-V" | "-v" => {
134                println!("FSM (Ferrix System Monitor) v{}", env!("CARGO_PKG_VERSION"));
135
136                eprintln!(" *** If you are from Russia, you can send me a donation:");
137                eprintln!("     2202 2062 5233 5406\n Thank you!");
138
139                Self::About
140            }
141            "export" => Self::Export,
142            _ => {
143                eprintln!("ERROR: Unknown page name: \"{value}\"!\n");
144                eprintln!(" *** If you are from Russia, you can send me a donation:");
145                eprintln!("     2202 2062 5233 5406\n Thank you!");
146
147                Self::default()
148            }
149        }
150    }
151}
152
153impl From<usize> for Page {
154    fn from(value: usize) -> Self {
155        match value {
156            0 => Self::Dashboard,
157            1 => Self::SystemMonitor,
158            2 => Self::Processors,
159            3 => Self::CPUFrequency,
160            4 => Self::CPUVulnerabilities,
161            5 => Self::Memory,
162            6 => Self::FileSystems,
163            7 => Self::Network,
164            8 => Self::DMI,
165            9 => Self::Battery,
166            10 => Self::Screen,
167            11 => Self::Sensors,
168            12 => Self::Distro,
169            13 => Self::Users,
170            14 => Self::Groups,
171            15 => Self::Environment,
172            16 => Self::SystemManager,
173            17 => Self::Software,
174            18 => Self::Kernel,
175            19 => Self::KModules,
176            20 => Self::SystemMisc,
177            21 => Self::Settings,
178            22 => Self::About,
179            _ => Page::Dashboard,
180        }
181    }
182}
183
184impl<'a> Page {
185    pub fn title(&'a self) -> iced::widget::Column<'a, Message> {
186        header_text(self.title_str())
187    }
188
189    pub fn page_num(&self) -> usize {
190        match self {
191            Self::Dashboard => 0,
192            Self::SystemMonitor => 1,
193            Self::Processors => 2,
194            Self::CPUFrequency => 3,
195            Self::CPUVulnerabilities => 4,
196            Self::Memory => 5,
197            Self::FileSystems => 6,
198            Self::Network => 7,
199            Self::DMI => 8,
200            Self::Battery => 9,
201            Self::Screen => 10,
202            Self::Sensors => 11,
203            Self::Distro => 12,
204            Self::Users => 13,
205            Self::Groups => 14,
206            Self::Environment => 15,
207            Self::SystemManager => 16,
208            Self::Software => 17,
209            Self::Kernel => 18,
210            Self::KModules => 19,
211            Self::SystemMisc => 20,
212            Self::Settings => 21,
213            Self::About => 22,
214            _ => 0,
215        }
216    }
217
218    pub fn next_page(&self) -> Self {
219        let mut id = self.page_num() + 1;
220        if id > Self::About.page_num() {
221            id = 0;
222        }
223        Self::from(id)
224    }
225
226    pub fn prev_page(&self) -> Self {
227        let cur_id = self.page_num();
228        let next_id = if cur_id == 0 {
229            Self::About.page_num()
230        } else {
231            cur_id - 1
232        };
233        Self::from(next_id)
234    }
235
236    pub fn scrolled_list_id(&self) -> Option<&'static str> {
237        match self {
238            Self::Processors => Some("proc-list"),
239            _ => None,
240        }
241    }
242
243    pub fn page_id(&self) -> &'static str {
244        match self {
245            Self::Dashboard => "dash",
246            Self::Processors => "proc",
247            Self::CPUFrequency => "cpufreq",
248            Self::CPUVulnerabilities => "cpuvuln",
249            Self::SystemMonitor => "sysmon",
250            Self::Memory => "mem",
251            Self::FileSystems => "fs",
252            Self::Network => "net",
253            Self::DMI => "dmi",
254            Self::Battery => "bat",
255            Self::Screen => "drm",
256            Self::Distro => "distro",
257            Self::SystemMisc => "sys",
258            Self::Users => "usr",
259            Self::Groups => "grp",
260            Self::SystemManager => "sysd",
261            Self::Software => "pkg",
262            Self::Environment => "env",
263            Self::Sensors => "hwmon",
264            Self::Kernel => "krn",
265            Self::KModules => "kmds",
266            Self::Development => "dev",
267            Self::Settings => "set",
268            Self::About => "about",
269            Self::Export => "export",
270            Self::Todo => "todo",
271        }
272    }
273
274    pub fn title_str(&self) -> String {
275        match self {
276            Self::Dashboard => fl!("page-dashboard"),
277            Self::Processors => fl!("page-procs"),
278            Self::CPUFrequency => fl!("page-cpufreq"),
279            Self::CPUVulnerabilities => fl!("page-vuln"),
280            Self::SystemMonitor => fl!("page-sysmon"),
281            Self::Memory => fl!("page-memory"),
282            Self::FileSystems => fl!("page-fsystems"),
283            Self::Network => fl!("page-net"),
284            Self::DMI => fl!("page-dmi"),
285            Self::Battery => fl!("page-battery"),
286            Self::Screen => fl!("page-screen"),
287            Self::Distro => fl!("page-distro"),
288            Self::Users => fl!("page-users"),
289            Self::Groups => fl!("page-groups"),
290            Self::SystemManager => fl!("page-sysmgr"),
291            Self::Software => fl!("page-software"),
292            Self::Environment => fl!("page-env"),
293            Self::Sensors => fl!("page-sensors"),
294            Self::Kernel => fl!("page-kernel"),
295            Self::KModules => fl!("page-kmods"),
296            Self::Development => fl!("page-dev"),
297            Self::SystemMisc => fl!("page-sysmisc"),
298            Self::Settings => fl!("page-settings"),
299            Self::About => fl!("page-about"),
300            Self::Export => fl!("page-export"),
301            Self::Todo => fl!("page-todo"),
302        }
303    }
304
305    pub fn page(&'a self, state: &'a Ferrix) -> Element<'a, Message> {
306        let page = match self {
307            Self::Dashboard => dashboard::dashboard(&state.data).into(),
308            Self::SystemMonitor => sysmon::usage_charts_page(
309                &state.data,
310                &state.data.curr_proc_stat,
311                &state.data.prev_proc_stat,
312            )
313            .into(), // TODO: cur_stat and proc_stat - ???
314            Self::Processors => {
315                cpu::proc_page(&state.data.proc_data, state.data.selected_proc).into()
316            }
317            Self::CPUFrequency => cpu_freq::cpu_freq_page(&state.data.cpu_freq).into(),
318            Self::CPUVulnerabilities => {
319                vulnerabilities::vulnerabilities_page(&state.data.cpu_vulnerabilities).into()
320            }
321            Self::Memory => ram::ram_page(&state.data.ram_data, &state.data.swap_data).into(),
322            Self::FileSystems => storage::storage_page(&state.data.storages).into(),
323            Self::Network => net::net_page(&state.data.networks).into(),
324            Self::DMI => dmi::dmi_page(&state.data.dmi_data).into(),
325            Self::Battery => battery::bat_page(&state.data.bat_data).into(),
326            Self::Screen => drm::drm_page(&state.data.drm_data).into(),
327            Self::Distro => distro::distro_page(&state.data.osrel_data).into(),
328            Self::Kernel => kernel::kernel_page(&state.data.kernel_data).into(),
329            Self::KModules => kernel::kmods_page(&state.data.kmods_data).into(),
330            Self::SystemMisc => system::system_page(&state.data.system).into(),
331            Self::Users => users::users_page(&state.data.users_list).into(),
332            Self::Groups => groups::groups_page(&state.data.groups_list).into(),
333            Self::SystemManager => systemd::services_page(
334                &state.data.boot_time,
335                &state.data.sysd_services_list,
336            )
337            .into(),
338            Self::Software => soft::soft_page(&state.data.installed_pkgs_list).into(),
339            Self::Environment => env::env_page(&state.data.system).into(),
340            Self::Settings => settings::settings_page(&state).into(),
341            Self::Export => export::export_page().into(),
342            Self::About => self.about_page().into(),
343            _ => self.todo_page(),
344        };
345
346        column![self.title(), page,].spacing(5).into()
347    }
348
349    fn todo_page(&self) -> Element<'a, Message> {
350        container(center(
351            text(fl!("page-todo-msg")).size(16).style(text::secondary),
352        ))
353        .into()
354    }
355
356    fn about_page(&'a self) -> container::Container<'a, Message> {
357        let img = iced::widget::svg("/usr/share/Ferrix/com.mskrasnov.Ferrix.svg")
358            .width(128)
359            .height(128);
360        let header = row![
361            img,
362            column![
363                text(fl!("about-hdr")).size(24),
364                text(format!(
365                    "{}: {}, {}: {}",
366                    fl!("about-ferrix"),
367                    env!("CARGO_PKG_VERSION"),
368                    fl!("about-flib"),
369                    ferrix_lib::FX_LIB_VERSION,
370                ))
371                .size(14),
372            ]
373            .spacing(5),
374        ]
375        .align_y(Center)
376        .spacing(5);
377
378        let about_info = row![
379            column![
380                text(fl!("about-author-hdr")).style(text::secondary),
381                text(fl!("about-feedback-hdr")).style(text::secondary),
382                text(fl!("about-source-hdr")).style(text::secondary),
383                text("crates.io:").style(text::secondary),
384                text(fl!("about-blog")).style(text::secondary),
385            ]
386            .align_x(Alignment::End)
387            .spacing(3),
388            column![
389                row![
390                    text(fl!("about-author")),
391                    link_button("(GitHub)", "https://github.com/mskrasnov"),
392                ]
393                .spacing(5),
394                link_button("mskrasnov07 at ya dot ru", "mailto:mskrasnov07@ya.ru"),
395                link_button("GitHub", "https://github.com/mskrasnov/Ferrix"),
396                row![
397                    link_button("ferrix-app", "https://crates.io/crates/ferrix-app"),
398                    text(", "),
399                    link_button("ferrix-lib", "https://crates.io/crates/ferrix-lib"),
400                ],
401                link_button("mskrasnov", "https://boosty.to/mskrasnov"),
402            ]
403            .spacing(3),
404        ]
405        .spacing(5);
406
407        let donate = column![
408            text(fl!("about-donate")),
409            link_button(fl!("about-donate-lbl"), "https://boosty.to/mskrasnov"),
410        ]
411        .spacing(5);
412
413        let contents = column![
414            column![header, rule::horizontal(1)].spacing(2),
415            about_info,
416            row![
417                text(fl!("about-support")).style(text::warning).size(16),
418                rule::horizontal(1)
419            ]
420            .align_y(Center)
421            .spacing(5),
422            donate,
423        ]
424        .spacing(5);
425
426        container(contents)
427    }
428}
429
430fn loading_page<'a>() -> container::Container<'a, Message> {
431    container(center(
432        text(fl!("ldr-page-tooltip"))
433            .style(text::secondary)
434            .size(14),
435    ))
436}
437
438fn error_page<'a>(etext: &'a str) -> container::Container<'a, Message> {
439    container(center(
440        column![
441            row![
442                iced::widget::svg(Handle::from_memory(ERROR_ICON))
443                    .width(20)
444                    .height(20),
445                text(fl!("err-page-tooltip")).size(20),
446            ]
447            .align_y(Center)
448            .spacing(5),
449            text(etext).style(text::secondary).size(14),
450        ]
451        .align_x(Center)
452        .spacing(5),
453    ))
454}