diff --git a/Cargo.lock b/Cargo.lock index cfcbb22..e1a0f1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -152,6 +152,7 @@ dependencies = [ "egui_glium", "egui_glow", "egui_vulkano", + "emath 0.18.0", "epaint 0.18.1", "epi", "glium 0.32.1", diff --git a/amdgpu/src/pidfile/ports.rs b/amdgpu/src/pidfile/ports.rs index d232ad0..21c5dfa 100644 --- a/amdgpu/src/pidfile/ports.rs +++ b/amdgpu/src/pidfile/ports.rs @@ -18,18 +18,170 @@ pub enum PortsError { Serialize(#[from] ron::Error), } +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub enum OutputType { + Reserved, + #[serde(rename = "v")] + Vga, + #[serde(rename = "m")] + MiniDvi, + #[serde(rename = "h")] + Hdmi, + #[serde(rename = "a")] + Audio, + #[serde(rename = "o")] + OpticalAudio, + #[serde(rename = "d")] + Dvi, + #[serde(rename = "t")] + Thunderbolt, + #[serde(rename = "D")] + DisplayPort, + #[serde(rename = "M")] + MiniDisplayPort, + #[serde(rename = "f")] + FireWire400, + #[serde(rename = "p")] + Ps2, + #[serde(rename = "s")] + Sata, + #[serde(rename = "e")] + ESata, + #[serde(rename = "E")] + Ethernet, + #[serde(rename = "F")] + FireWire800, + #[serde(rename = "1")] + UsbTypeA, + #[serde(rename = "2")] + UsbTypeB, + #[serde(rename = "3")] + UsbTypeC, + #[serde(rename = "4")] + MicroUsb, + #[serde(rename = "5")] + MimiUsb, +} + +impl OutputType { + pub fn to_coords(&self) -> (u32, u32) { + match self { + OutputType::Reserved => (0, 0), + // + OutputType::Vga => (0, 0), + OutputType::MiniDvi => (80, 0), + OutputType::Hdmi => (160, 0), + OutputType::Audio => (240, 0), + OutputType::OpticalAudio => (320, 0), + // + OutputType::Dvi => (0, 80), + OutputType::Thunderbolt => (80, 80), + OutputType::DisplayPort => (160, 80), + OutputType::MiniDisplayPort => (240, 80), + OutputType::FireWire400 => (320, 80), + // + OutputType::Ps2 => (0, 160), + OutputType::Sata => (80, 160), + OutputType::ESata => (160, 160), + OutputType::Ethernet => (240, 160), + OutputType::FireWire800 => (320, 160), + // + OutputType::UsbTypeA => (0, 240), + OutputType::UsbTypeB => (80, 240), + OutputType::UsbTypeC => (160, 240), + OutputType::MicroUsb => (240, 240), + OutputType::MimiUsb => (320, 240), + } + } + pub fn name(&self) -> &str { + match self { + OutputType::Reserved => "-----", + // + OutputType::Vga => "Vga", + OutputType::MiniDvi => "MiniDvi", + OutputType::Hdmi => "Hdmi", + OutputType::Audio => "Audio", + OutputType::OpticalAudio => "OptimalAudio", + // + OutputType::Dvi => "Dvi", + OutputType::Thunderbolt => "Thunderbolt", + OutputType::DisplayPort => "DisplayPort", + OutputType::MiniDisplayPort => "MiniDisplayPort", + OutputType::FireWire400 => "FireWire400", + // + OutputType::Ps2 => "Ps2", + OutputType::Sata => "Sata", + OutputType::ESata => "ESata", + OutputType::Ethernet => "Ethernet", + OutputType::FireWire800 => "FireWire800", + // + OutputType::UsbTypeA => "UsbTypeA", + OutputType::UsbTypeB => "UsbTypeB", + OutputType::UsbTypeC => "UsbTypeC", + OutputType::MicroUsb => "MicroUsb", + OutputType::MimiUsb => "MimiUsb", + } + } + + pub fn parse_str(s: &str) -> Option { + Some(match s { + "DP" => Self::DisplayPort, + "eDP" => Self::MiniDisplayPort, + "HDMI" => Self::Hdmi, + _ => return None, + }) + } + + pub fn all() -> [OutputType; 20] { + [ + OutputType::Vga, + OutputType::MiniDvi, + OutputType::Hdmi, + OutputType::Audio, + OutputType::OpticalAudio, + OutputType::Dvi, + OutputType::Thunderbolt, + OutputType::DisplayPort, + OutputType::MiniDisplayPort, + OutputType::FireWire400, + OutputType::Ps2, + OutputType::Sata, + OutputType::ESata, + OutputType::Ethernet, + OutputType::FireWire800, + OutputType::UsbTypeA, + OutputType::UsbTypeB, + OutputType::UsbTypeC, + OutputType::MicroUsb, + OutputType::MimiUsb, + ] + } +} + #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct Output { #[serde(rename = "c")] pub card: String, #[serde(rename = "t")] pub port_type: String, + #[serde(rename = "T")] + pub ty: Option, #[serde(rename = "m")] pub port_name: Option, #[serde(rename = "n")] pub port_number: u8, #[serde(rename = "s")] pub status: Status, + #[serde(rename = "M")] + pub modes: Vec, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct OutputMode { + #[serde(rename = "w")] + pub width: u16, + #[serde(rename = "h")] + pub height: u16, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/amdguid/Cargo.toml b/amdguid/Cargo.toml index b7c6dfe..6171e4c 100644 --- a/amdguid/Cargo.toml +++ b/amdguid/Cargo.toml @@ -65,6 +65,7 @@ parking_lot = { version = "0.12" } nix = { version = "0.25" } image = { version = "0.24.2" } +emath = { version = "0.18" } [dev-dependencies] amdgpu = { path = "../amdgpu", version = "1.0", features = ["gui-helper"] } diff --git a/amdguid/assets/icons/ports2.jpg b/amdguid/assets/icons/ports2.jpg new file mode 100644 index 0000000..ac8cc0f Binary files /dev/null and b/amdguid/assets/icons/ports2.jpg differ diff --git a/amdguid/src/app.rs b/amdguid/src/app.rs index 1f8dfc4..d2e64c4 100644 --- a/amdguid/src/app.rs +++ b/amdguid/src/app.rs @@ -1,11 +1,12 @@ use std::collections::{BTreeMap, HashMap}; use std::sync::Arc; -use amdgpu::pidfile::ports::Output; +use amdgpu::pidfile::ports::{Output, OutputType}; use amdgpu::pidfile::Pid; use egui::Ui; +use epaint::ColorImage; use epi::Frame; -use image::{ImageBuffer, ImageFormat, RgbaImage}; +use image::{GenericImageView, ImageBuffer, ImageFormat}; use parking_lot::Mutex; use crate::widgets::outputs_settings::OutputsSettings; @@ -83,113 +84,55 @@ static RELOAD_PID_LIST_DELAY: u8 = 18; #[cfg(debug_assertions)] static RELOAD_PID_LIST_DELAY: u8 = 80; -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub enum ImageType { - Vga, - MiniDvi, - Hdmi, - Audio, - OptimalAudio, - Dvi, - Thunderbolt, - DisplayPort, - MiniDisplayPort, - FireWire400, - Ps2, - Sata, - ESata, - Ethernet, - FireWire800, - UsbTypeA, - UsbTypeB, - UsbTypeC, - MicroUsb, - MimiUsb, -} - -impl ImageType { - pub fn to_coords(&self) -> (u32, u32) { - match self { - ImageType::Vga => (0, 0), - ImageType::MiniDvi => (160, 0), - ImageType::Hdmi => (320, 0), - ImageType::Audio => (480, 0), - ImageType::OptimalAudio => (640, 0), - // - ImageType::Dvi => (0, 160), - ImageType::Thunderbolt => (160, 160), - ImageType::DisplayPort => (320, 160), - ImageType::MiniDisplayPort => (480, 160), - ImageType::FireWire400 => (640, 160), - // - ImageType::Ps2 => (0, 320), - ImageType::Sata => (160, 320), - ImageType::ESata => (320, 320), - ImageType::Ethernet => (480, 320), - ImageType::FireWire800 => (640, 320), - // - ImageType::UsbTypeA => (0, 480), - ImageType::UsbTypeB => (160, 480), - ImageType::UsbTypeC => (320, 480), - ImageType::MicroUsb => (480, 480), - ImageType::MimiUsb => (640, 480), - } - } -} - pub struct StatefulConfig { pub config: FanConfig, pub state: ChangeState, - pub images: HashMap, + pub textures: HashMap, } impl StatefulConfig { pub fn new(config: FanConfig) -> Self { - let compact = image::load_from_memory_with_format( - include_bytes!("../assets/icons/ports.jpg"), - ImageFormat::Jpeg, - ) - .unwrap() - .into_rgba8(); - let images = [ - ImageType::Vga, - ImageType::MiniDvi, - ImageType::Hdmi, - ImageType::Audio, - ImageType::OptimalAudio, - ImageType::Dvi, - ImageType::Thunderbolt, - ImageType::DisplayPort, - ImageType::MiniDisplayPort, - ImageType::FireWire400, - ImageType::Ps2, - ImageType::Sata, - ImageType::ESata, - ImageType::Ethernet, - ImageType::FireWire800, - ImageType::UsbTypeA, - ImageType::UsbTypeB, - ImageType::UsbTypeC, - ImageType::MicroUsb, - ImageType::MimiUsb, - ] - .iter() - .fold(HashMap::with_capacity(20), |mut memo, ty| { - let (offset_x, offset_y) = ty.to_coords(); - let mut part = ImageBuffer::new(160, 160); - for x in 0..160 { - for y in 0..160 { - part.put_pixel(x, y, compact.get_pixel(x + offset_x, y + offset_y).clone()); - } - } - memo.insert(*ty, part); - memo - }); + let textures = HashMap::with_capacity(40); Self { config, state: ChangeState::New, - images, + textures, + } + } + + pub fn load_textures(&mut self, ui: &mut Ui) { + if !self.textures.is_empty() { + return; + } + + // 80x80 + let image = { + let bytes = include_bytes!("../assets/icons/ports2.jpg"); + image::load_from_memory_with_format(bytes, ImageFormat::Jpeg).unwrap() + }; + + let ctx = ui.ctx(); + + for ty in OutputType::all() { + let (offset_x, offset_y) = ty.to_coords(); + let mut img = ImageBuffer::new(80, 80); + for x in 0..80 { + for y in 0..80 { + img.put_pixel(x, y, image.get_pixel(x + offset_x, y + offset_y)); + } + } + + let size = [img.width() as _, img.height() as _]; + let pixels = img.as_flat_samples(); + let id = ctx.load_texture( + String::from(ty.name()), + epaint::ImageData::Color(ColorImage::from_rgba_unmultiplied( + size, + pixels.as_slice(), + )), + ); + self.textures.insert(ty, id); } } } @@ -228,6 +171,8 @@ impl AmdGui { } pub fn ui(&mut self, ui: &mut Ui) { + self.config.load_textures(ui); + match self.page { Page::Config => { self.change_fan_settings @@ -277,7 +222,7 @@ impl AmdGui { let mut names = outputs.iter().fold( Vec::with_capacity(outputs.len()), |mut set, output| { - set.push(format!("{}", output.card)); + set.push(output.card.clone()); set }, ); diff --git a/amdguid/src/backend/glium_backend.rs b/amdguid/src/backend/glium_backend.rs index 40e987c..6a2142b 100644 --- a/amdguid/src/backend/glium_backend.rs +++ b/amdguid/src/backend/glium_backend.rs @@ -1,10 +1,13 @@ +use std::collections::HashMap; +use std::rc::Rc; use std::sync::Arc; use glium::glutin; +use image::RgbaImage; use parking_lot::Mutex; use tokio::sync::mpsc::UnboundedReceiver; -use crate::app::AmdGui; +use crate::app::{AmdGui, ImageStorage, ImageType}; use crate::backend::create_ui; fn create_display(event_loop: &glutin::event_loop::EventLoop<()>) -> glium::Display { diff --git a/amdguid/src/backend/mod.rs b/amdguid/src/backend/mod.rs index 4deda97..7570164 100644 --- a/amdguid/src/backend/mod.rs +++ b/amdguid/src/backend/mod.rs @@ -10,12 +10,12 @@ use std::sync::Arc; use egui::panel::TopBottomSide; use egui::{Layout, PointerButton}; #[cfg(feature = "xorg-glium")] -pub use glium_backend::run_app; +pub use glium_backend::*; #[cfg(feature = "xorg-glow")] -pub use glow_backend::run_app; +pub use glow_backend::*; use parking_lot::Mutex; #[cfg(feature = "wayland")] -pub use wayland_backend::run_app; +pub use wayland_backend::*; use crate::app::Page; use crate::AmdGui; diff --git a/amdguid/src/backend/wayland_backend.rs b/amdguid/src/backend/wayland_backend.rs index d3acf3f..74a00f8 100644 --- a/amdguid/src/backend/wayland_backend.rs +++ b/amdguid/src/backend/wayland_backend.rs @@ -81,7 +81,7 @@ pub fn run_app(amd_gui: Arc>, _receiver: UnboundedReceiver) let event_loop = EventLoop::new(); let surface = WindowBuilder::new() - .with_title("egui_vulkano demo") + .with_title("AMD GUID") .with_fullscreen(Some(Fullscreen::Borderless(None))) .build_vk_surface(&event_loop, instance.clone()) .unwrap(); @@ -176,32 +176,34 @@ pub fn run_app(amd_gui: Arc>, _receiver: UnboundedReceiver) }; mod vs { + #![allow(clippy::needless_question_mark)] vulkano_shaders::shader! { ty: "vertex", src: " - #version 450 +#version 450 - layout(location = 0) in vec2 position; +layout(location = 0) in vec2 position; - void main() { - gl_Position = vec4(position, 0.0, 1.0); - } - " +void main() { + gl_Position = vec4(position, 0.0, 1.0); +} + " } } mod fs { + #![allow(clippy::needless_question_mark)] vulkano_shaders::shader! { ty: "fragment", src: " - #version 450 +#version 450 - layout(location = 0) out vec4 f_color; +layout(location = 0) out vec4 f_color; - void main() { - f_color = vec4(1.0, 0.0, 0.0, 1.0); - } - " +void main() { + f_color = vec4(1.0, 0.0, 0.0, 1.0); +} + " } } @@ -220,7 +222,7 @@ pub fn run_app(amd_gui: Arc>, _receiver: UnboundedReceiver) }, passes: [ { color: [color], depth_stencil: {}, input: [] }, - { color: [color], depth_stencil: {}, input: [] } // Create a second renderpass to draw egui + { color: [color], depth_stencil: {}, input: [] } // Create a second render-pass to draw egui ] ) .unwrap(); @@ -231,7 +233,7 @@ pub fn run_app(amd_gui: Arc>, _receiver: UnboundedReceiver) .input_assembly_state(InputAssemblyState::new()) .viewport_state(ViewportState::viewport_dynamic_scissor_irrelevant()) .fragment_shader(fs.entry_point("main").unwrap(), ()) - .render_pass(Subpass::from(render_pass.clone().into(), 0).unwrap()) + .render_pass(Subpass::from(render_pass.clone(), 0).unwrap()) .build(device.clone()) .unwrap(); @@ -241,7 +243,8 @@ pub fn run_app(amd_gui: Arc>, _receiver: UnboundedReceiver) depth_range: 0.0..1.0, }; - let mut framebuffers = window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); + let mut frame_buffers = + window_size_dependent_setup(&images, render_pass.clone(), &mut viewport); let mut recreate_swapchain = false; @@ -261,9 +264,6 @@ pub fn run_app(amd_gui: Arc>, _receiver: UnboundedReceiver) //Set up some window to look at for the test - // let mut my_texture = egui_ctx.load_texture("my_texture", - // ColorImage::example()); - event_loop.run(move |event, _, control_flow| { match event { Event::WindowEvent { @@ -304,7 +304,7 @@ pub fn run_app(amd_gui: Arc>, _receiver: UnboundedReceiver) }; swapchain = new_swapchain; - framebuffers = window_size_dependent_setup( + frame_buffers = window_size_dependent_setup( &new_images, render_pass.clone(), &mut viewport, @@ -352,7 +352,7 @@ pub fn run_app(amd_gui: Arc>, _receiver: UnboundedReceiver) // Do your usual rendering builder .begin_render_pass( - framebuffers[image_num].clone(), + frame_buffers[image_num].clone(), SubpassContents::Inline, clear_values, ) diff --git a/amdguid/src/items.rs b/amdguid/src/items.rs index 064a704..b92a1d7 100644 --- a/amdguid/src/items.rs +++ b/amdguid/src/items.rs @@ -143,5 +143,5 @@ pub struct ExplicitGenerator { #[inline(always)] pub fn y_intersection(p1: &Pos2, p2: &Pos2, y: f32) -> Option { ((p1.y > y && p2.y < y) || (p1.y < y && p2.y > y)) - .then(|| ((y * (p1.x - p2.x)) - (p1.x * p2.y - p1.y * p2.x)) / (p1.y - p2.y)) + .then_some(((y * (p1.x - p2.x)) - (p1.x * p2.y - p1.y * p2.x)) / (p1.y - p2.y)) } diff --git a/amdguid/src/items/points.rs b/amdguid/src/items/points.rs index 97ea387..ef10952 100644 --- a/amdguid/src/items/points.rs +++ b/amdguid/src/items/points.rs @@ -118,7 +118,7 @@ impl PlotItem for Points { let default_stroke = Stroke::new(stroke_size, *color); let mut stem_stroke = default_stroke; let stroke = (!filled) - .then(|| default_stroke) + .then_some(default_stroke) .unwrap_or_else(Stroke::none); let fill = filled.then(|| *color).unwrap_or_default(); diff --git a/amdguid/src/items/values.rs b/amdguid/src/items/values.rs index 0ea6cf5..00d3426 100644 --- a/amdguid/src/items/values.rs +++ b/amdguid/src/items/values.rs @@ -126,7 +126,7 @@ impl Values { ) -> Option> { let start = range1.start().max(*range2.start()); let end = range1.end().min(*range2.end()); - (start < end).then(|| start..=end) + (start < end).then_some(start..=end) } pub(crate) fn get_bounds(&self) -> Bounds { diff --git a/amdguid/src/widgets/change_fan_settings.rs b/amdguid/src/widgets/change_fan_settings.rs index 027ff69..1a26558 100644 --- a/amdguid/src/widgets/change_fan_settings.rs +++ b/amdguid/src/widgets/change_fan_settings.rs @@ -149,10 +149,12 @@ impl ChangeFanSettings { }); } + #[allow(clippy::explicit_auto_deref)] fn save_config(config: FanConfig, state: &mut StatefulConfig) { state.state = ChangeState::Reloading; let config = config.lock(); + let c: &amdgpu_config::fan::Config = &*config; let content = match toml::to_string(c) { Err(e) => { diff --git a/amdguid/src/widgets/cooling_performance.rs b/amdguid/src/widgets/cooling_performance.rs index 1999783..4bf815c 100644 --- a/amdguid/src/widgets/cooling_performance.rs +++ b/amdguid/src/widgets/cooling_performance.rs @@ -15,6 +15,7 @@ pub struct CoolingPerformance { } impl CoolingPerformance { + #[allow(clippy::explicit_auto_deref)] pub fn new(capacity: usize, fan_config: FanConfig) -> Self { let amd_mon = amdgpu::hw_mon::open_hw_mon(Card(0)) .map(|hw| amdmond_lib::AmdMon::wrap(hw, &*fan_config.lock())) diff --git a/amdguid/src/widgets/drag_plot_prepared.rs b/amdguid/src/widgets/drag_plot_prepared.rs index 61b19fe..3fc34c8 100644 --- a/amdguid/src/widgets/drag_plot_prepared.rs +++ b/amdguid/src/widgets/drag_plot_prepared.rs @@ -260,6 +260,7 @@ impl DragPlotPrepared { } }; + #[allow(clippy::explicit_auto_deref)] shapes.push(Shape::text( &*ui.fonts(), pointer + vec2(3.0, -2.0), diff --git a/amdguid/src/widgets/legend_widget.rs b/amdguid/src/widgets/legend_widget.rs index 6f3258f..78ddc6e 100644 --- a/amdguid/src/widgets/legend_widget.rs +++ b/amdguid/src/widgets/legend_widget.rs @@ -43,7 +43,7 @@ impl LegendWidget { LegendEntry::new(color, checked) }); }); - (!entries.is_empty()).then(|| Self { + (!entries.is_empty()).then_some(Self { rect, entries, config, diff --git a/amdguid/src/widgets/output_widget.rs b/amdguid/src/widgets/output_widget.rs index e74ed72..7dfef90 100644 --- a/amdguid/src/widgets/output_widget.rs +++ b/amdguid/src/widgets/output_widget.rs @@ -1,69 +1,58 @@ -use std::collections::HashMap; - use amdgpu::pidfile::ports::Output; use egui::{Response, Sense, Ui, Vec2}; use epaint::Stroke; -pub struct OutputWidget<'output, 'textures> { +use crate::app::StatefulConfig; + +pub struct OutputWidget<'output, 'stateful> { output: &'output Output, - textures: &'textures HashMap, + state: &'stateful mut StatefulConfig, } -impl<'output, 'textures> OutputWidget<'output, 'textures> { - pub fn new( - output: &'output Output, - textures: &'textures HashMap, - ) -> Self { - Self { output, textures } +impl<'output, 'stateful> OutputWidget<'output, 'stateful> { + pub fn new(output: &'output Output, state: &'stateful mut StatefulConfig) -> Self { + Self { output, state } } } -impl<'output, 'textures> egui::Widget for OutputWidget<'output, 'textures> { +impl<'output, 'stateful> egui::Widget for OutputWidget<'output, 'stateful> { fn ui(self, ui: &mut Ui) -> Response { - let (rect, res) = ui.allocate_exact_size(Vec2::new(80.0, 70.0), Sense::click()); - // let _transform = ScreenTransform::new(rect, Bounds::new_symmetrical(1.0), - // false, false); let _frame = *transform.frame(); - // eprintln!("min {:?} max {:?}", frame.min, frame.max); - let painter = ui.painter(); //.sub_region(*transform.frame()); + let (rect, res) = ui.allocate_exact_size(Vec2::new(80.0, 80.0), Sense::click()); + if let Some(handle) = self.output.ty.and_then(|ty| self.state.textures.get(&ty)) { + ui.image(handle.id(), handle.size_vec2()); + } else { + let painter = ui.painter(); + painter.rect_filled(rect, 0.0, epaint::color::Color32::DARK_RED); + painter.rect(rect, 2.0, epaint::color::Color32::DARK_RED, { + Stroke { + width: 1.0, + color: epaint::color::Color32::GREEN, + } + }); - painter.rect_filled(rect, 0.0, epaint::color::Color32::DARK_RED); - painter.rect(rect, 2.0, epaint::color::Color32::DARK_RED, { - let mut s = Stroke::default(); - s.color = epaint::color::Color32::GREEN; - s.width = 1.0; - s - }); + let rect_middle_point = (rect.max - rect.min) / 2.0; - let rect_middle_point = (rect.max - rect.min) / 2.0; + painter.circle_filled( + rect.min + Vec2::new(rect_middle_point.x / 2.0, rect_middle_point.y), + 3.0, + epaint::color::Color32::GREEN, + ); + painter.circle_filled( + rect.min + rect_middle_point, + 3.0, + epaint::color::Color32::GREEN, + ); - painter.circle_filled( - rect.min + Vec2::new(rect_middle_point.x / 2.0, rect_middle_point.y), - 3.0, - epaint::color::Color32::GREEN, - ); - painter.circle_filled( - rect.min + rect_middle_point, - 3.0, - epaint::color::Color32::GREEN, - ); - - painter.circle_filled( - rect.min - + Vec2::new( - rect_middle_point.x + (rect_middle_point.x / 2.0), - rect_middle_point.y, - ), - 3.0, - epaint::color::Color32::GREEN, - ); + painter.circle_filled( + rect.min + + Vec2::new( + rect_middle_point.x + (rect_middle_point.x / 2.0), + rect_middle_point.y, + ), + 3.0, + epaint::color::Color32::GREEN, + ); + } res } } - -fn icon(name: &str, textures: &HashMap) { - if let Some(_texture) = textures.get(name) { - // - } else { - // - } -} diff --git a/amdguid/src/widgets/outputs_settings.rs b/amdguid/src/widgets/outputs_settings.rs index 4a4ddbd..a29955a 100644 --- a/amdguid/src/widgets/outputs_settings.rs +++ b/amdguid/src/widgets/outputs_settings.rs @@ -1,52 +1,22 @@ -use std::collections::{BTreeMap, HashMap}; +use std::collections::BTreeMap; use amdgpu::pidfile::ports::{Output, Status}; -use egui::Ui; -use epaint::ColorImage; -use image::ImageFormat; +use egui::{RichText, Ui, WidgetText}; +use epaint::Color32; use crate::app::StatefulConfig; use crate::widgets::output_widget::OutputWidget; #[derive(Default)] -pub struct OutputsSettings { - textures: HashMap, -} +pub struct OutputsSettings {} impl OutputsSettings { pub fn draw( &mut self, ui: &mut Ui, - _state: &mut StatefulConfig, + state: &mut StatefulConfig, outputs: &BTreeMap>, ) { - outputs.values().flatten().for_each(|output| { - // 160x160 - let image = { - let bytes = include_bytes!("../../assets/icons/ports.jpg"); - image::load_from_memory(bytes).unwrap() - }; - - for (_idx, _pixel, _p) in image.to_rgba8().enumerate_pixels() { - // let bytes = pixel.; - // eprintln!("{:?}", bytes); - } - - if !self.textures.contains_key(&output.port_type) { - let img = image::load_from_memory_with_format( - include_bytes!("../../assets/icons/ports.jpg"), - ImageFormat::Jpeg, - ) - .unwrap(); - let image_buffer = img.to_rgba8(); - let size = [image.width() as _, image.height() as _]; - let pixels = image_buffer.as_flat_samples(); - let _ = ui.ctx().load_texture( - output.port_type.clone(), - ColorImage::from_rgba_unmultiplied(size, pixels.as_slice()), - ); - } - }); let _available = ui.available_rect_before_wrap(); ui.vertical(|ui| { @@ -57,7 +27,7 @@ impl OutputsSettings { ui.horizontal_top(|ui| { outputs.iter().for_each(|output| { ui.vertical(|ui| { - ui.add(OutputWidget::new(output, &self.textures)); + ui.add(OutputWidget::new(output, state)); ui.label(format!("port_number {}", output.port_number)); ui.label(format!("port_type {:?}", output.port_type)); @@ -66,17 +36,27 @@ impl OutputsSettings { "port_name {}", output.port_name.as_deref().unwrap_or_default() )); - ui.label(match output.status { - Status::Connected => "Connected", - Status::Disconnected => "Disconnected", - }); + + let state = WidgetText::RichText( + RichText::new(match output.status { + Status::Connected => "Connected", + Status::Disconnected => "Disconnected", + }) + .color( + match output.status { + Status::Connected => Color32::DARK_GREEN, + Status::Disconnected => Color32::GRAY, + }, + ), + ); + + ui.label(state); }); }); }); }); }); }); - // eprintln!("=============================================================="); }); } } diff --git a/amdportsd/src/main.rs b/amdportsd/src/main.rs index a8769ba..07241ba 100644 --- a/amdportsd/src/main.rs +++ b/amdportsd/src/main.rs @@ -19,9 +19,25 @@ fn parse_output(entry: DirEntry) -> Option { .map(String::from) .collect::>() .into_iter(); + + let modes = std::fs::read_to_string(entry.path().join("modes")) + .unwrap_or_default() + .lines() + .filter_map(|s| { + let mut it = s.split('x'); + let width = it.next().and_then(|s| s.parse::().ok())?; + let height = it.next().and_then(|s| s.parse::().ok())?; + Some(OutputMode { width, height }) + }) + .collect::>(); + + let card = it.next()?.strip_prefix("card")?.to_string(); + let port_type = it.next()?; let mut output = Output { - card: it.next()?.strip_prefix("card")?.to_string(), - port_type: it.next()?, + card, + ty: OutputType::parse_str(&port_type), + port_type, + modes, ..Default::default() }; let mut it = it.rev();