diff --git a/src/debug.rs b/src/debug.rs index 51493de0..84f3f1a7 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -4,25 +4,43 @@ use crate::state::{Common, Fps}; use smithay::utils::{Logical, Rectangle}; use smithay_egui::EguiFrame; -pub fn debug_ui( - state: &mut Common, - fps: &Fps, +pub fn fps_ui( + state: &Common, + fps: &mut Fps, area: Rectangle, scale: f64, - primary: bool, ) -> EguiFrame { - let size = area.size; - let alpha = state.egui.alpha; + use egui::widgets::plot::{Bar, BarChart, HLine, Legend, Plot}; - state.egui.state.run( + let (max, min, avg, avg_fps) = ( + fps.max_frametime().as_secs_f64(), + fps.min_frametime().as_secs_f64(), + fps.avg_frametime().as_secs_f64(), + fps.avg_fps(), + ); + let bars = fps + .frames + .iter() + .rev() + .take(30) + .rev() + .enumerate() + .map(|(i, (_, d))| { + let value = d.as_secs_f64(); + let transformed = ((value - min) / (max - min) * 255.0).round() as u8; + Bar::new(i as f64, transformed as f64).fill(egui::Color32::from_rgb( + transformed, + 255 - transformed, + 0, + )) + }) + .collect(); + + fps.state.run( |ctx| { egui::Area::new("main") .anchor(egui::Align2::LEFT_TOP, (10.0, 10.0)) .show(ctx, |ui| { - use egui::widgets::plot::{Bar, BarChart, HLine, Legend, Plot}; - - // Basics - let label_res = ui.label(format!( "cosmic-comp version {}", std::env!("CARGO_PKG_VERSION") @@ -34,43 +52,15 @@ pub fn debug_ui( if !state.egui.active { ui.label("Press Mod+Escape for debug menu"); } else { - ui.set_max_width(label_res.rect.width()); + ui.set_max_width(label_res.rect.min.x + label_res.rect.width()); ui.separator(); - // FPS - - let (max, min, avg, avg_fps) = ( - fps.max_frametime().as_secs_f64(), - fps.min_frametime().as_secs_f64(), - fps.avg_frametime().as_secs_f64(), - fps.avg_fps(), - ); - ui.label(egui::RichText::new(format!("FPS: {:>7.3}", avg_fps)).heading()); ui.label("Frame Times:"); ui.label(egui::RichText::new(format!("avg: {:>7.6}", avg)).code()); ui.label(egui::RichText::new(format!("min: {:>7.6}", min)).code()); ui.label(egui::RichText::new(format!("max: {:>7.6}", max)).code()); - let fps_chart = BarChart::new( - fps.frames - .iter() - .rev() - .take(30) - .rev() - .enumerate() - .map(|(i, d)| { - let value = d.as_secs_f64(); - let transformed = - ((value - min) / (max - min) * 255.0).round() as u8; - Bar::new(i as f64, value).fill(egui::Color32::from_rgb( - transformed, - 255 - transformed, - 0, - )) - }) - .collect(), - ) - .vertical(); + let fps_chart = BarChart::new(bars).vertical(); Plot::new("FPS") .legend(Legend::default()) @@ -78,7 +68,7 @@ pub fn debug_ui( .include_x(0.0) .include_x(30.0) .include_y(0.0) - .include_y(max * 2.0) + .include_y(300.0) .show_x(false) .show(ui, |plot_ui| { plot_ui.bar_chart(fps_chart); @@ -86,117 +76,133 @@ pub fn debug_ui( HLine::new(avg).highlight().color(egui::Color32::LIGHT_BLUE), ); }); - - ui.separator(); - - // Toggles and stuff - ui.add( - egui::Slider::new(&mut state.egui.alpha, 0.1..=1.0) - .clamp_to_range(true) - .text("Opacity"), - ); - ui.checkbox(&mut state.egui.spaces, "Workspace UI"); - //TODO: ui.checkbox(&mut state.egui.outputs, "Outputs UI"); } }); + }, + area, + scale, + 1.0, + &state.start_time, + fps.modifiers.clone(), + ) +} - // don't show these one others then the primary monitor - if primary { - egui::Window::new("Workspaces") - .open(&mut state.egui.spaces) - .vscroll(true) - .show(ctx, |ui| { - use crate::shell::workspaces::{ActiveWorkspace, Mode, MAX_WORKSPACES}; +pub fn debug_ui( + state: &mut Common, + area: Rectangle, + scale: f64, +) -> Option { + if !state.egui.active { + return None; + } - ui.set_min_width(250.0); + Some(state.egui.state.run( + |ctx| { + egui::Window::new("Workspaces") + .default_pos([0.0, 300.0]) + .vscroll(true) + .collapsible(true) + .show(ctx, |ui| { + use crate::shell::workspaces::{ActiveWorkspace, Mode, MAX_WORKSPACES}; - // Mode + ui.set_min_width(250.0); - ui.label(egui::RichText::new("Mode").heading()); - let mut mode = *state.spaces.mode(); - let active = if let Mode::Global { active } = mode { - active - } else { - 0 - }; - ui.radio_value(&mut mode, Mode::OutputBound, "Output bound"); - ui.radio_value(&mut mode, Mode::Global { active }, "Global"); - state.spaces.set_mode(mode); + // Mode - match *state.spaces.mode() { - Mode::OutputBound => { - ui.label("Workspaces:"); - for output in state.spaces.outputs().cloned().collect::>() { - ui.horizontal(|ui| { - let active = output - .user_data() - .get::() - .unwrap() - .get() - .unwrap(); - let mut active_val = active as f64; - ui.label(output.name()); - ui.add( - egui::DragValue::new(&mut active_val) - .clamp_range(0..=(MAX_WORKSPACES - 1)) - .speed(1.0), - ); - if active != active_val as usize { - state.spaces.activate(&output, active_val as usize); - } - }); - } - } - Mode::Global { active } => { + ui.label(egui::RichText::new("Mode").heading()); + let mut mode = *state.spaces.mode(); + let active = if let Mode::Global { active } = mode { + active + } else { + 0 + }; + ui.radio_value(&mut mode, Mode::OutputBound, "Output bound"); + ui.radio_value(&mut mode, Mode::Global { active }, "Global"); + state.spaces.set_mode(mode); + + match *state.spaces.mode() { + Mode::OutputBound => { + ui.label("Workspaces:"); + for output in state.spaces.outputs().cloned().collect::>() { ui.horizontal(|ui| { + let active = output + .user_data() + .get::() + .unwrap() + .get() + .unwrap(); let mut active_val = active as f64; - ui.label("Workspace:"); + ui.label(output.name()); ui.add( egui::DragValue::new(&mut active_val) .clamp_range(0..=(MAX_WORKSPACES - 1)) .speed(1.0), ); if active != active_val as usize { - let output = - state.spaces.outputs().next().cloned().unwrap(); state.spaces.activate(&output, active_val as usize); } }); } } - - // Spaces - for (i, space) in state.spaces.spaces.iter().enumerate() { - ui.collapsing(format!("Space: {}", i), |ui| { - ui.collapsing(format!("Windows"), |ui| { - for window in space.windows() { - ui.collapsing(format!("{:?}", window.toplevel()), |ui| { - ui.label(format!( - "Rect: {:?}", - space.window_geometry(window) - )); - ui.label(format!( - "Bounding box: {:?}", - space.window_bbox(window) - )); - }); - } - }) + Mode::Global { active } => { + ui.horizontal(|ui| { + let mut active_val = active as f64; + ui.label("Workspace:"); + ui.add( + egui::DragValue::new(&mut active_val) + .clamp_range(0..=(MAX_WORKSPACES - 1)) + .speed(1.0), + ); + if active != active_val as usize { + let output = state.spaces.outputs().next().cloned().unwrap(); + state.spaces.activate(&output, active_val as usize); + } }); } - }); + } - egui::Window::new("Outputs") - .open(&mut state.egui.outputs) - .hscroll(true) - .show(ctx, |ui| {}); - } + // Spaces + for (i, space) in state.spaces.spaces.iter().enumerate() { + ui.collapsing(format!("Space: {}", i), |ui| { + ui.collapsing(format!("Windows"), |ui| { + for window in space.windows() { + ui.collapsing(format!("{:?}", window.toplevel()), |ui| { + ui.label(format!( + "Rect: {:?}", + space.window_geometry(window) + )); + ui.label(format!( + "Bounding box: {:?}", + space.window_bbox(window) + )); + }); + } + }) + }); + } + }); + + egui::Window::new("Outputs") + .collapsible(true) + .hscroll(true) + .default_pos([300.0, 300.0]) + .show(ctx, |ui| { + ui.label(format!("Global Space: {:?}", state.spaces.global_space())); + for output in state.spaces.outputs().cloned().collect::>().into_iter() { + ui.separator(); + ui.collapsing(output.name(), |ui| { + ui.label(format!("Output: {:#?}", output)); + ui.label(format!("Geometry: {:?}", state.spaces.output_geometry(&output))); + ui.label(format!("Local Geometry: {:?}", state.spaces.active_space(&output).output_geometry(&output))); + ui.label(format!("Relative Geometry: {:?}", state.spaces.space_relative_output_geometry((0, 0), &output))); + }); + } + }); }, area, - size.to_f64().to_physical(scale).to_i32_round(), scale, - alpha, + state.egui.alpha, &state.start_time, state.egui.modifiers.clone(), - ) + )) } diff --git a/src/state.rs b/src/state.rs index 3b5b4aa8..269786cb 100644 --- a/src/state.rs +++ b/src/state.rs @@ -60,14 +60,14 @@ pub struct Egui { pub modifiers: smithay::wayland::seat::ModifiersState, pub active: bool, pub alpha: f32, - pub spaces: bool, - pub outputs: bool, } #[cfg(feature = "debug")] pub struct Fps { - pub frames: VecDeque, - pub last: Instant, + pub state: smithay_egui::EguiState, + pub modifiers: smithay::wayland::seat::ModifiersState, + pub frames: VecDeque<(Instant, Duration)>, + pub start: Instant, } pub enum BackendData { @@ -163,12 +163,10 @@ impl State { #[cfg(feature = "debug")] egui: Egui { - state: smithay_egui::EguiState::new(), + state: smithay_egui::EguiState::new(smithay_egui::EguiMode::Continuous), modifiers: Default::default(), active: false, alpha: 1.0, - outputs: false, - spaces: false, }, }, backend: BackendData::Unset, @@ -180,38 +178,59 @@ impl State { impl Fps { const WINDOW_SIZE: usize = 100; - pub fn tick(&mut self) { - let next = Instant::now(); - let frame_time = next.duration_since(self.last); + pub fn start(&mut self) { + self.start = Instant::now(); + } - self.frames.push_back(frame_time); + pub fn end(&mut self) { + let frame_time = Instant::now().duration_since(self.start); + + self.frames.push_back((self.start, frame_time)); if self.frames.len() > Fps::WINDOW_SIZE { self.frames.pop_front(); } - self.last = next; } pub fn max_frametime(&self) -> &Duration { - self.frames.iter().max().unwrap_or(&Duration::ZERO) + self.frames + .iter() + .map(|(_, f)| f) + .max() + .unwrap_or(&Duration::ZERO) } pub fn min_frametime(&self) -> &Duration { - self.frames.iter().min().unwrap_or(&Duration::ZERO) + self.frames + .iter() + .map(|(_, f)| f) + .min() + .unwrap_or(&Duration::ZERO) } pub fn avg_frametime(&self) -> Duration { if self.frames.is_empty() { return Duration::ZERO; } - self.frames.iter().cloned().sum::() / (self.frames.len() as u32) + self.frames + .iter() + .map(|(_, f)| f) + .cloned() + .sum::() + / (self.frames.len() as u32) } pub fn avg_fps(&self) -> f64 { if self.frames.is_empty() { return 0.0; } - let sum_secs = self.frames.iter().map(|d| d.as_secs_f64()).sum::(); - 1.0 / (sum_secs / self.frames.len() as f64) + let secs = match (self.frames.front(), self.frames.back()) { + (Some((start, _)), Some((end, dur))) => { + end.duration_since(*start) + *dur + } + _ => Duration::ZERO, + } + .as_secs_f64(); + 1.0 / (secs / self.frames.len() as f64) } } @@ -219,8 +238,16 @@ impl Fps { impl Default for Fps { fn default() -> Fps { Fps { + state: { + let state = smithay_egui::EguiState::new(smithay_egui::EguiMode::Continuous); + let mut visuals: egui::style::Visuals = Default::default(); + visuals.window_shadow.extrusion = 0.0; + state.context().set_visuals(visuals); + state + }, + modifiers: Default::default(), frames: VecDeque::with_capacity(Fps::WINDOW_SIZE + 1), - last: Instant::now(), + start: Instant::now(), } } }