use std::collections::HashMap;
use super::clients::frame_bytes;
use super::{ClientId, Server};
use crate::graphics;
const BUDGET: usize = 4 << 20;
#[derive(Default)]
pub(super) struct ClientImages {
answered: bool,
name: Option<String>,
capable: bool,
cell: Option<(u16, u16)>,
sent: HashMap<u32, u64>,
version: Option<u64>,
}
impl ClientImages {
pub(super) fn forget(&mut self) {
self.sent.clear();
self.version = None;
}
}
impl Server {
pub(super) fn probe_graphics(&mut self, id: ClientId) {
if !self.env.graphics.enabled() {
return;
}
self.images.entry(id).or_default();
if let Some(c) = self.clients.get(&id) {
let _ = c.out.try_send(frame_bytes(graphics::probe().as_bytes()));
}
}
pub(super) fn probe_cell_size(&mut self, id: ClientId) {
if self.env.graphics.enabled()
&& let Some(c) = self.clients.get(&id)
{
let _ = c.out.try_send(frame_bytes(b"\x1b[16t"));
}
}
pub(super) fn graphics_reply(&mut self, id: ClientId, reply: &str) -> bool {
let answer = graphics::probe_answer(reply);
let name = graphics::terminal_name(reply);
if answer.is_some() || name.is_some() {
let Some(ci) = self.images.get_mut(&id) else { return true };
if answer == Some(true) {
ci.answered = true;
}
if name.is_some() {
ci.name = name;
}
let capable = ci.answered && ci.name.as_deref().is_some_and(graphics::draws_placeholders);
if capable && !ci.capable {
ci.capable = true;
ci.forget();
self.update_availability();
if let Some(c) = self.clients.get_mut(&id) {
c.dirty = true;
}
}
return true;
}
if let Some((w, h)) = graphics::cell_size_answer(reply) {
if let Some(ci) = self.images.get_mut(&id) {
ci.cell = Some((w, h));
}
if self.primary.is_none_or(|p| p == id) && self.env.graphics.cell() != (w, h) {
self.env.graphics.set_cell(w, h);
for p in self.panes.values_mut() {
p.refresh_pixel_size();
}
}
return true;
}
false
}
pub(super) fn graphics_farewell(&mut self, id: ClientId) {
let Some(ci) = self.images.get_mut(&id) else { return };
let mut out = Vec::new();
for kid in ci.sent.keys() {
graphics::delete(*kid, &mut out);
}
ci.forget();
if let Some(c) = self.clients.get(&id)
&& !out.is_empty()
{
let _ = c.out.try_send(frame_bytes(&out));
}
}
pub(super) fn graphics_client_gone(&mut self, id: ClientId) {
if self.images.remove(&id).is_some() {
self.update_availability();
}
if let Some(cell) = self.primary.and_then(|p| self.images.get(&p)).and_then(|ci| ci.cell) {
self.env.graphics.set_cell(cell.0, cell.1);
}
}
fn update_availability(&mut self) {
let any = self.images.iter().any(|(id, ci)| ci.capable && self.clients.get(id).is_some_and(|c| c.attached));
self.env.graphics.set_available(any);
}
pub(super) fn can_show_images(&self, id: ClientId) -> bool {
self.images.get(&id).is_some_and(|ci| ci.capable)
}
pub(super) fn graphics_updates(&mut self, id: ClientId) -> (Vec<u8>, bool) {
let mut out = Vec::new();
let version = self.env.graphics.version();
let Some(ci) = self.images.get(&id) else { return (out, false) };
if !ci.capable || ci.version == Some(version) {
return (out, false);
}
let live: HashMap<u32, graphics::Live> = self
.panes
.values()
.filter_map(|p| p.term())
.flat_map(|t| t.graphics.shown())
.map(|(kid, s)| (kid, (s.generation, s.picture.clone(), s.placements.clone())))
.collect();
let Some(ci) = self.images.get_mut(&id) else { return (out, false) };
let more = graphics::update_terminal(&mut ci.sent, live, BUDGET, &mut out);
if !more {
ci.version = Some(version);
}
(out, more)
}
}