309 lines
9.2 KiB
Rust
309 lines
9.2 KiB
Rust
use {
|
|
crate::{
|
|
async_engine::AsyncEngine,
|
|
cmm::{cmm_manager::ColorManager, cmm_render_intent::RenderIntent},
|
|
fixed::Fixed,
|
|
ifs::wl_output::WlOutputGlobal,
|
|
rect::{Rect, Region},
|
|
renderer::renderer_base::RendererBase,
|
|
state::State,
|
|
theme::Color,
|
|
time::Time,
|
|
tree::Transform,
|
|
utils::{asyncevent::AsyncEvent, errorfmt::ErrorFmt, timer::TimerFd},
|
|
},
|
|
isnt::std_1::primitive::IsntSliceExt,
|
|
std::{
|
|
cell::{Cell, RefCell},
|
|
collections::VecDeque,
|
|
rc::Rc,
|
|
time::Duration,
|
|
},
|
|
uapi::c::CLOCK_MONOTONIC,
|
|
};
|
|
|
|
pub async fn visualize_damage(state: Rc<State>) {
|
|
let timer = match TimerFd::new(CLOCK_MONOTONIC) {
|
|
Ok(t) => t,
|
|
Err(e) => {
|
|
log::error!("Could not create timer fd: {}", ErrorFmt(e));
|
|
return;
|
|
}
|
|
};
|
|
loop {
|
|
state.damage_visualizer.entry_added.triggered().await;
|
|
let duration = Duration::from_millis(50);
|
|
let res = timer.program(Some(duration), Some(duration));
|
|
if let Err(e) = res {
|
|
log::error!("Could not program timer: {}", ErrorFmt(e));
|
|
return;
|
|
}
|
|
loop {
|
|
let res = timer.expired(&state.ring).await;
|
|
if let Err(e) = res {
|
|
log::error!("Could not wait for timer to expire: {}", ErrorFmt(e));
|
|
return;
|
|
}
|
|
if state.damage_visualizer.entries.borrow_mut().is_empty() {
|
|
break;
|
|
}
|
|
damage_all(&state);
|
|
}
|
|
let res = timer.program(None, None);
|
|
if let Err(e) = res {
|
|
log::error!("Could not disable timer: {}", ErrorFmt(e));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn damage_all(state: &State) {
|
|
for connector in state.connectors.lock().values() {
|
|
if connector.connected.get() {
|
|
let damage = &mut *connector.damage.borrow_mut();
|
|
damage.clear();
|
|
damage.push(connector.damage_intersect.get());
|
|
connector.damage();
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct DamageVisualizer {
|
|
eng: Rc<AsyncEngine>,
|
|
entries: RefCell<VecDeque<Damage>>,
|
|
entry_added: AsyncEvent,
|
|
enabled: Cell<bool>,
|
|
decay: Cell<Duration>,
|
|
color: Cell<Color>,
|
|
color_manager: Rc<ColorManager>,
|
|
}
|
|
|
|
const MAX_RECTS: usize = 100_000;
|
|
|
|
struct Damage {
|
|
time: Time,
|
|
rect: Rect,
|
|
}
|
|
|
|
impl DamageVisualizer {
|
|
pub fn new(eng: &Rc<AsyncEngine>, color_manager: &Rc<ColorManager>) -> Self {
|
|
Self {
|
|
eng: eng.clone(),
|
|
entries: Default::default(),
|
|
entry_added: Default::default(),
|
|
enabled: Default::default(),
|
|
decay: Cell::new(Duration::from_secs(2)),
|
|
color: Cell::new(Color::from_srgba_straight(255, 0, 0, 128)),
|
|
color_manager: color_manager.clone(),
|
|
}
|
|
}
|
|
|
|
pub fn add(&self, rect: Rect) {
|
|
if !self.enabled.get() {
|
|
return;
|
|
}
|
|
let entries = &mut *self.entries.borrow_mut();
|
|
if entries.is_empty() {
|
|
self.entry_added.trigger();
|
|
}
|
|
entries.push_back(Damage {
|
|
time: self.eng.now(),
|
|
rect,
|
|
});
|
|
if entries.len() > MAX_RECTS {
|
|
entries.pop_front();
|
|
}
|
|
}
|
|
|
|
pub fn set_enabled(&self, state: &State, enabled: bool) {
|
|
self.enabled.set(enabled);
|
|
if !enabled {
|
|
self.entries.borrow_mut().clear();
|
|
damage_all(state);
|
|
}
|
|
}
|
|
|
|
pub fn set_decay(&self, decay: Duration) {
|
|
let millis = decay.as_millis();
|
|
if millis == 0 || millis > u64::MAX as u128 {
|
|
return;
|
|
}
|
|
self.decay.set(decay);
|
|
}
|
|
|
|
pub fn set_color(&self, color: Color) {
|
|
self.color.set(color);
|
|
}
|
|
|
|
fn trim(&self) {
|
|
let now = self.eng.now();
|
|
let entries = &mut *self.entries.borrow_mut();
|
|
let decay = self.decay.get();
|
|
while let Some(first) = entries.front() {
|
|
if now - first.time >= decay {
|
|
entries.pop_front();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn render(&self, cursor_rect: &Rect, renderer: &mut RendererBase<'_>) {
|
|
if !self.enabled.get() {
|
|
return;
|
|
}
|
|
self.trim();
|
|
let now = self.eng.now();
|
|
let entries = &*self.entries.borrow();
|
|
let decay = self.decay.get();
|
|
let base_color = self.color.get();
|
|
let mut used = Region::default();
|
|
let dx = -cursor_rect.x1();
|
|
let dy = -cursor_rect.y1();
|
|
let decay_millis = decay.as_millis() as u64 as f32;
|
|
renderer.sync();
|
|
let srgb = &self.color_manager.srgb_gamma22().linear;
|
|
for entry in entries.iter().rev() {
|
|
let region = Region::new(entry.rect);
|
|
let region = region.subtract_cow(&used);
|
|
if region.is_not_empty() {
|
|
let age = (now - entry.time).as_millis() as u64 as f32 / decay_millis;
|
|
let color = base_color * (1.0 - age);
|
|
renderer.fill_boxes2(
|
|
region.rects(),
|
|
&color,
|
|
srgb,
|
|
RenderIntent::Perceptual,
|
|
dx,
|
|
dy,
|
|
);
|
|
used = used.union_cow(®ion).into_owned();
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn copy_damage(&self, output: &WlOutputGlobal) {
|
|
if !self.enabled.get() {
|
|
return;
|
|
}
|
|
self.trim();
|
|
let entries = &*self.entries.borrow();
|
|
let pos = output.pos.get();
|
|
for entry in entries {
|
|
if entry.rect.intersects(&pos) {
|
|
output.add_damage_area(&entry.rect);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub struct DamageMatrix {
|
|
transform: Transform,
|
|
mx: f64,
|
|
my: f64,
|
|
dx: f64,
|
|
dy: f64,
|
|
smear: i32,
|
|
}
|
|
|
|
impl Default for DamageMatrix {
|
|
fn default() -> Self {
|
|
Self {
|
|
transform: Default::default(),
|
|
mx: 1.0,
|
|
my: 1.0,
|
|
dx: 0.0,
|
|
dy: 0.0,
|
|
smear: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl DamageMatrix {
|
|
pub fn apply(&self, dx: i32, dy: i32, rect: Rect) -> Rect {
|
|
let x1 = rect.x1() - self.smear;
|
|
let x2 = rect.x2() + self.smear;
|
|
let y1 = rect.y1() - self.smear;
|
|
let y2 = rect.y2() + self.smear;
|
|
let [x1, y1, x2, y2] = match self.transform {
|
|
Transform::None => [x1, y1, x2, y2],
|
|
Transform::Rotate90 => [-y2, x1, -y1, x2],
|
|
Transform::Rotate180 => [-x2, -y2, -x1, -y1],
|
|
Transform::Rotate270 => [y1, -x2, y2, -x1],
|
|
Transform::Flip => [-x2, y1, -x1, y2],
|
|
Transform::FlipRotate90 => [y1, x1, y2, x2],
|
|
Transform::FlipRotate180 => [x1, -y2, x2, -y1],
|
|
Transform::FlipRotate270 => [-y2, -x2, -y1, -x1],
|
|
};
|
|
let x1 = (x1 as f64 * self.mx + self.dx).floor() as i32 + dx;
|
|
let y1 = (y1 as f64 * self.my + self.dy).floor() as i32 + dy;
|
|
let x2 = (x2 as f64 * self.mx + self.dx).ceil() as i32 + dx;
|
|
let y2 = (y2 as f64 * self.my + self.dy).ceil() as i32 + dy;
|
|
Rect::new_saturating(x1, y1, x2, y2)
|
|
}
|
|
|
|
pub fn new(
|
|
transform: Transform,
|
|
legacy_scale: i32,
|
|
buffer_width: i32,
|
|
buffer_height: i32,
|
|
viewport: Option<[Fixed; 4]>,
|
|
dst_width: i32,
|
|
dst_height: i32,
|
|
) -> DamageMatrix {
|
|
let mut buffer_width = buffer_width as f64;
|
|
let mut buffer_height = buffer_height as f64;
|
|
let dst_width = dst_width as f64;
|
|
let dst_height = dst_height as f64;
|
|
|
|
let mut mx = 1.0;
|
|
let mut my = 1.0;
|
|
if legacy_scale != 1 {
|
|
let scale_inv = 1.0 / (legacy_scale as f64);
|
|
mx = scale_inv;
|
|
my = scale_inv;
|
|
buffer_width *= scale_inv;
|
|
buffer_height *= scale_inv;
|
|
}
|
|
let (mut buffer_width, mut buffer_height) =
|
|
transform.maybe_swap((buffer_width, buffer_height));
|
|
let (mut dx, mut dy) = match transform {
|
|
Transform::None => (0.0, 0.0),
|
|
Transform::Rotate90 => (buffer_width, 0.0),
|
|
Transform::Rotate180 => (buffer_width, buffer_height),
|
|
Transform::Rotate270 => (0.0, buffer_height),
|
|
Transform::Flip => (buffer_width, 0.0),
|
|
Transform::FlipRotate90 => (0.0, 0.0),
|
|
Transform::FlipRotate180 => (0.0, buffer_height),
|
|
Transform::FlipRotate270 => (buffer_width, buffer_height),
|
|
};
|
|
if let Some([x, y, w, h]) = viewport {
|
|
dx -= x.to_f64();
|
|
dy -= y.to_f64();
|
|
buffer_width = w.to_f64();
|
|
buffer_height = h.to_f64();
|
|
}
|
|
let mut smear = false;
|
|
if dst_width != buffer_width {
|
|
let scale = dst_width / buffer_width;
|
|
mx *= scale;
|
|
dx *= scale;
|
|
smear |= dst_width > buffer_width;
|
|
}
|
|
if dst_height != buffer_height {
|
|
let scale = dst_height / buffer_height;
|
|
my *= scale;
|
|
dy *= scale;
|
|
smear |= dst_height > buffer_height;
|
|
}
|
|
DamageMatrix {
|
|
transform,
|
|
mx,
|
|
my,
|
|
dx,
|
|
dy,
|
|
smear: smear as _,
|
|
}
|
|
}
|
|
}
|