use crate::base::{Point, Rect, Size};
use crate::gfx::{choose_channel, Channel, ExternalSink};
use crate::render::{Cell, Surface};
use super::driver::Driver;
pub(super) struct BufSink<'a>(pub(super) &'a mut Vec<(Vec<u8>, Point)>);
impl ExternalSink for BufSink<'_> {
fn external_write(&mut self, bytes: &[u8], at: Point) {
self.0.push((bytes.to_vec(), at));
}
}
impl Driver {
pub(super) fn steal_root_surface(&mut self) -> Surface {
let mut store = self.overlays.store().borrow_mut();
let i = store
.index_of(crate::app::overlays::ROOT_LAYER_ID)
.expect("root layer exists after Driver::new");
std::mem::replace(
store.layers[i].surface_mut(),
Surface::new(Size::ZERO, Cell::EMPTY),
)
}
pub(super) fn restore_root_surface(&mut self, surface: Surface) {
let mut store = self.overlays.store().borrow_mut();
if let Some(i) = store.index_of(crate::app::overlays::ROOT_LAYER_ID) {
*store.layers[i].surface_mut() = surface;
}
}
pub(super) fn pre_image_pass(&mut self, damage: &mut Vec<Rect>) {
let gfx_caps = self.caps.graphics();
let next_channel = choose_channel(&gfx_caps);
let (retired, moved): (Vec<u64>, Vec<u64>) = {
let mut store = self.overlays.store().borrow_mut();
let retired: Vec<u64> = store.retired_images.drain(..).collect();
let moved = store
.images
.iter()
.filter(|e| e.dirty)
.filter(|e| {
self.image_session
.slot_info(e.id)
.is_some_and(|(ch, rect)| ch != next_channel || rect != e.rect)
})
.map(|e| e.id)
.collect();
(retired, moved)
};
let mut vacated: Vec<(Channel, Rect)> = Vec::new();
for key in moved {
vacated.extend(self.image_session.slot_info(key));
}
for key in retired {
vacated.extend(self.image_session.slot_info(key));
let mut sink = BufSink(&mut self.pending_image_bytes);
self.image_session.release(&mut sink, key, &gfx_caps);
}
for &(channel, rect) in &vacated {
damage.push(rect);
if matches!(channel, Channel::Iterm2 | Channel::Sixel) {
self.poison_prev_rect(rect);
}
}
if damage.is_empty() {
return;
}
let repair: Vec<u64> = {
let store = self.overlays.store().borrow();
store
.images
.iter()
.filter(|e| {
match self.image_session.slot_info(e.id) {
Some((Channel::Kitty, _)) | None => false,
Some((Channel::Mosaic, _)) => damage.iter().any(|r| r.intersects(e.rect)),
Some((Channel::Iterm2 | Channel::Sixel, _)) => {
vacated.iter().any(|&(_, r)| r.intersects(e.rect))
}
}
})
.map(|e| e.id)
.collect()
};
for key in repair {
self.image_session.invalidate_slot(key);
let mut store = self.overlays.store().borrow_mut();
if let Some(e) = store.images.iter_mut().find(|e| e.id == key) {
e.dirty = true;
}
}
}
pub(super) fn render_images(&mut self, root_surface: &mut Surface) {
struct ImageJob {
key: u64,
version: u64,
rect: Rect,
bitmap: crate::gfx::Bitmap,
}
let jobs: Vec<ImageJob> = {
let mut store = self.overlays.store().borrow_mut();
store
.images
.iter_mut()
.filter(|e| e.dirty)
.map(|e| {
e.dirty = false;
ImageJob {
key: e.id,
version: e.version,
rect: e.rect,
bitmap: e.bitmap.clone(),
}
})
.collect()
};
if jobs.is_empty() {
return;
}
let gfx_caps = self.caps.graphics();
let mut warnings: Vec<String> = Vec::new();
let mut sink = BufSink(&mut self.pending_image_bytes);
for job in jobs {
match self.image_session.sync(
&mut sink,
job.key,
job.version,
&job.bitmap,
job.rect,
&gfx_caps,
) {
crate::gfx::SyncOutcome::Cells(rendered) => {
warnings.extend(rendered.warnings);
if let crate::gfx::ImageOutput::Cells(patches) = rendered.output {
root_surface.blit_mosaic(
patches.iter().map(|p| (p.pos, p.ch, p.fg, p.bg)),
Point::ZERO,
);
}
}
crate::gfx::SyncOutcome::Emitted(rendered) => warnings.extend(rendered.warnings),
crate::gfx::SyncOutcome::Unchanged => {}
}
}
for w in warnings {
if self.image_notice_seen.insert(w.clone()) {
self.pending_image_notices.push(w);
}
}
}
}