1mod cells;
26mod dirty;
27mod pixels;
28
29use alloc::rc::{Rc, Weak};
30use alloc::string::String;
31use alloc::vec;
32use alloc::vec::Vec;
33use core::cell::RefCell;
34use core::marker::PhantomData;
35
36pub use cells::Cell;
37pub(crate) use cells::write_text_clipped;
38use dirty::DirtyRects;
39pub(crate) use pixels::Canvas;
40
41use crate::error::{Error, Result};
42use crate::geometry::{Geometry, Rect};
43use crate::host;
44use crate::local::instance_local;
45
46pub const HEADER_LEN: u32 = 32;
48pub const CELL_LEN: u32 = 16;
50pub const PIXEL_LEN: u32 = 4;
52
53const HEADER_WORDS: usize = (HEADER_LEN / 4) as usize;
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
57pub enum Model {
58 Cells,
59 Pixels,
60}
61
62impl Model {
63 pub fn as_str(self) -> &'static str {
64 match self {
65 Self::Cells => "cells",
66 Self::Pixels => "pixels",
67 }
68 }
69
70 fn word(self) -> u32 {
71 match self {
72 Self::Cells => 0,
73 Self::Pixels => 1,
74 }
75 }
76
77 fn unit_words(self) -> usize {
79 match self {
80 Self::Cells => (CELL_LEN / 4) as usize,
81 Self::Pixels => 1,
82 }
83 }
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq)]
88pub(crate) struct RegionLayout {
89 pub len: u32,
90 pub header_len: u32,
91 pub slot_len: u32,
92 pub model: Model,
93 pub geometry: Geometry,
94}
95
96impl RegionLayout {
97 pub(crate) fn new(model: Model, geometry: Geometry) -> Self {
99 let slot_len = match model {
100 Model::Cells => geometry.cols * geometry.rows * CELL_LEN,
101 Model::Pixels => geometry.px_w * geometry.px_h * PIXEL_LEN,
102 };
103 Self {
104 len: HEADER_LEN + 2 * slot_len,
105 header_len: HEADER_LEN,
106 slot_len,
107 model,
108 geometry,
109 }
110 }
111
112 fn extent(&self) -> (u32, u32) {
114 match self.model {
115 Model::Cells => (self.geometry.cols, self.geometry.rows),
116 Model::Pixels => (self.geometry.px_w, self.geometry.px_h),
117 }
118 }
119
120 fn slot_words(&self) -> usize {
121 (self.slot_len / 4) as usize
122 }
123}
124
125mod sealed {
126 pub trait Sealed {}
127}
128
129pub trait SurfaceModel: sealed::Sealed {
131 const MODEL: Model;
132}
133
134#[derive(Clone, Copy, Debug)]
136pub enum Cells {}
137
138#[derive(Clone, Copy, Debug)]
141pub enum Pixels {}
142
143impl sealed::Sealed for Cells {}
144impl sealed::Sealed for Pixels {}
145
146impl SurfaceModel for Cells {
147 const MODEL: Model = Model::Cells;
148}
149
150impl SurfaceModel for Pixels {
151 const MODEL: Model = Model::Pixels;
152}
153
154#[derive(Clone, Debug, PartialEq, Eq)]
156pub struct CommitInfo {
157 pub seq: u32,
159 pub slot: u8,
161 pub dirty: Vec<Rect>,
163 pub full: bool,
165}
166
167#[derive(Debug)]
168enum Backend {
169 Host,
171 Offscreen(Geometry),
173}
174
175#[derive(Debug)]
176struct Binding {
177 layout: RegionLayout,
178 region: Vec<u32>,
180 back: u8,
182}
183
184#[derive(Debug)]
185pub(crate) struct Inner {
186 id: String,
187 model: Model,
188 backend: Backend,
189 binding: Option<Binding>,
190 retired: Option<Vec<u32>>,
193 dirty: DirtyRects,
194 sync: Vec<Rect>,
197 full: bool,
199 seq: u32,
200 error: Option<Error>,
201 last_commit: Option<CommitInfo>,
202}
203
204instance_local! {
205 fn registry() -> Vec<Weak<RefCell<Inner>>> = Vec::new();
206}
207
208pub(crate) fn rebind_surfaces(id: &str) {
210 let surfaces: Vec<Rc<RefCell<Inner>>> = registry(|registry| {
211 registry.retain(|weak| weak.strong_count() > 0);
212 registry.iter().filter_map(Weak::upgrade).collect()
213 });
214 for surface in surfaces {
215 let mut inner = surface.borrow_mut();
216 if inner.id == id {
217 inner.rebind();
218 }
219 }
220}
221
222fn as_bytes(words: &[u32]) -> &[u8] {
223 unsafe { core::slice::from_raw_parts(words.as_ptr().cast::<u8>(), words.len() * 4) }
226}
227
228fn as_bytes_mut(words: &mut [u32]) -> &mut [u8] {
229 unsafe { core::slice::from_raw_parts_mut(words.as_mut_ptr().cast::<u8>(), words.len() * 4) }
231}
232
233impl Inner {
234 fn new(id: String, model: Model, backend: Backend) -> Self {
235 Self {
236 id,
237 model,
238 backend,
239 binding: None,
240 retired: None,
241 dirty: DirtyRects::default(),
242 sync: Vec::new(),
243 full: false,
244 seq: 0,
245 error: None,
246 last_commit: None,
247 }
248 }
249
250 fn rebind(&mut self) {
251 match self.bind() {
252 Ok(_) => self.error = None,
253 Err(error) => self.error = Some(error),
254 }
255 }
256
257 fn bind(&mut self) -> Result<bool> {
260 let layout = match &self.backend {
261 Backend::Host => host::surface_layout(&self.id)?,
262 Backend::Offscreen(geometry) => RegionLayout::new(self.model, *geometry),
263 };
264 if layout.model != self.model {
265 return Err(Error::WrongModel {
266 surface: self.id.clone(),
267 expected: self.model,
268 found: layout.model,
269 });
270 }
271 if layout.geometry.is_empty() {
272 return Ok(false);
273 }
274 let mut region = vec![0u32; (layout.len as usize).div_ceil(4)];
275 let g = layout.geometry;
276 region[..HEADER_WORDS].copy_from_slice(&[
277 self.seq,
278 layout.model.word(),
279 g.cols,
280 g.rows,
281 g.px_w,
282 g.px_h,
283 g.cell_px_w,
284 g.cell_px_h,
285 ]);
286 if let Backend::Host = self.backend {
287 host::surface_attach(&self.id, &as_bytes(®ion)[..layout.len as usize])?;
288 }
289 if let Some(previous) = self.binding.take() {
290 if self.retired.is_none() {
294 self.retired = Some(previous.region);
295 }
296 }
297 self.binding = Some(Binding {
298 layout,
299 region,
300 back: 0,
301 });
302 self.full = true;
303 self.dirty.clear();
304 self.sync.clear();
305 Ok(true)
306 }
307
308 fn ensure_bound(&mut self) -> Result<&mut Binding> {
309 if self.binding.is_none() {
310 if let Some(error) = self.error.clone() {
311 return Err(error);
312 }
313 self.bind()?;
314 }
315 let id = &self.id;
316 self.binding.as_mut().ok_or_else(|| Error::NotAttached {
317 surface: id.clone(),
318 })
319 }
320
321 fn extent(&self) -> (u32, u32) {
322 self.binding
323 .as_ref()
324 .map_or((0, 0), |binding| binding.layout.extent())
325 }
326
327 fn apply_sync(&mut self) {
329 let Some(binding) = self.binding.as_mut() else {
330 self.sync.clear();
331 return;
332 };
333 if self.sync.is_empty() {
334 return;
335 }
336 let (width, _) = binding.layout.extent();
337 let unit = binding.layout.model.unit_words();
338 let slot_words = binding.layout.slot_words();
339 let front = HEADER_WORDS + usize::from(binding.back ^ 1) * slot_words;
340 let back = HEADER_WORDS + usize::from(binding.back) * slot_words;
341 for rect in self.sync.drain(..) {
342 for y in rect.y..rect.bottom() {
343 let start = (y as usize * width as usize + rect.x as usize) * unit;
344 let len = rect.w as usize * unit;
345 binding
346 .region
347 .copy_within(front + start..front + start + len, back + start);
348 }
349 }
350 }
351
352 fn back(&mut self) -> Option<(&mut [u32], RegionLayout, &mut DirtyRects)> {
355 self.apply_sync();
356 let binding = self.binding.as_mut()?;
357 let slot_words = binding.layout.slot_words();
358 let start = HEADER_WORDS + usize::from(binding.back) * slot_words;
359 Some((
360 &mut binding.region[start..start + slot_words],
361 binding.layout,
362 &mut self.dirty,
363 ))
364 }
365
366 fn back_discard(&mut self) -> Option<(&mut [u32], RegionLayout)> {
369 self.sync.clear();
370 let binding = self.binding.as_mut()?;
371 let (width, height) = binding.layout.extent();
372 self.dirty
373 .add(Rect::new(0, 0, width, height), width, height);
374 let slot_words = binding.layout.slot_words();
375 let start = HEADER_WORDS + usize::from(binding.back) * slot_words;
376 Some((
377 &mut binding.region[start..start + slot_words],
378 binding.layout,
379 ))
380 }
381
382 fn mark_dirty(&mut self, rect: Rect) {
383 self.apply_sync();
384 let (width, height) = self.extent();
385 self.dirty.add(rect, width, height);
386 }
387
388 fn commit(&mut self) -> Result<bool> {
389 self.ensure_bound()?;
390 let (width, height) = self.extent();
391 let full = self.full;
392 let rects = if full {
393 self.dirty.clear();
394 if width == 0 || height == 0 {
395 Vec::new()
396 } else {
397 vec![Rect::new(0, 0, width, height)]
398 }
399 } else {
400 self.dirty.take()
401 };
402 if rects.is_empty() {
403 return Ok(false);
404 }
405 let seq = self.seq.wrapping_add(1);
406 let Some(binding) = self.binding.as_mut() else {
407 return Ok(false);
408 };
409 binding.region[0] = seq;
410 let slot = binding.back;
411 if let Backend::Host = self.backend
412 && let Err(error) = host::surface_commit(&self.id, slot, &rects)
413 {
414 for rect in rects {
415 self.dirty.add(rect, width, height);
416 }
417 return Err(error);
418 }
419 binding.back ^= 1;
420 self.seq = seq;
421 self.full = false;
422 self.retired = None;
423 self.sync = rects.clone();
424 self.last_commit = Some(CommitInfo {
425 seq,
426 slot,
427 dirty: rects,
428 full,
429 });
430 Ok(true)
431 }
432}
433
434#[derive(Debug)]
436pub struct Surface<M: SurfaceModel> {
437 inner: Rc<RefCell<Inner>>,
438 model: PhantomData<M>,
439}
440
441impl<M: SurfaceModel> Surface<M> {
442 pub fn new(id: &str) -> Self {
445 let inner = Rc::new(RefCell::new(Inner::new(id.into(), M::MODEL, Backend::Host)));
446 registry(|registry| registry.push(Rc::downgrade(&inner)));
447 inner.borrow_mut().rebind();
448 Self {
449 inner,
450 model: PhantomData,
451 }
452 }
453
454 pub fn offscreen(geometry: Geometry) -> Self {
458 let mut inner = Inner::new("offscreen".into(), M::MODEL, Backend::Offscreen(geometry));
459 inner.rebind();
460 Self {
461 inner: Rc::new(RefCell::new(inner)),
462 model: PhantomData,
463 }
464 }
465
466 pub fn resize_offscreen(&mut self, geometry: Geometry) {
470 let mut inner = self.inner.borrow_mut();
471 if let Backend::Offscreen(current) = &mut inner.backend {
472 *current = geometry;
473 inner.rebind();
474 }
475 }
476
477 pub fn id(&self) -> String {
478 self.inner.borrow().id.clone()
479 }
480
481 pub fn geometry(&self) -> Option<Geometry> {
483 let inner = self.inner.borrow();
484 inner
485 .binding
486 .as_ref()
487 .map(|binding| binding.layout.geometry)
488 }
489
490 pub fn size(&self) -> (u32, u32) {
492 self.inner.borrow().extent()
493 }
494
495 pub fn request_size(&self, cols: u32, rows: u32) -> Result<()> {
499 crate::host::surface_request_size(&self.id(), cols, rows)
500 }
501
502 pub fn set_label(&self, label: Option<&str>) -> Result<()> {
506 crate::host::surface_set_label(&self.id(), label)
507 }
508
509 pub fn set_caret(&self, caret: Option<(u32, u32)>) -> Result<()> {
513 crate::host::surface_set_caret(&self.id(), caret)
514 }
515
516 pub fn is_attached(&self) -> bool {
517 self.inner.borrow().binding.is_some()
518 }
519
520 pub fn is_dirty(&self) -> bool {
523 let inner = self.inner.borrow();
524 inner.binding.is_some() && (inner.full || !inner.dirty.is_empty())
525 }
526
527 pub fn mark_dirty(&mut self, rect: Rect) {
529 self.inner.borrow_mut().mark_dirty(rect);
530 }
531
532 pub fn commit(&mut self) -> Result<bool> {
536 self.inner.borrow_mut().commit()
537 }
538
539 pub fn last_commit(&self) -> Option<CommitInfo> {
541 self.inner.borrow().last_commit.clone()
542 }
543
544 pub fn pending_dirty(&self) -> Vec<Rect> {
547 self.inner.borrow().dirty.rects().to_vec()
548 }
549
550 pub fn detach(&mut self) -> Result<()> {
552 let mut inner = self.inner.borrow_mut();
553 if inner.binding.take().is_some()
554 && let Backend::Host = inner.backend
555 {
556 inner.retired = None;
557 return host::surface_detach(&inner.id);
558 }
559 Ok(())
560 }
561}
562
563impl<M: SurfaceModel> Drop for Surface<M> {
564 fn drop(&mut self) {
565 let _ = self.detach();
568 }
569}
570
571#[derive(Debug)]
577pub struct AnySurface {
578 id: String,
579 surface: Option<AnySurfaceInner>,
580}
581
582#[derive(Debug)]
583enum AnySurfaceInner {
584 Cells(Surface<Cells>),
585 Pixels(Surface<Pixels>),
586}
587
588#[derive(Debug)]
590pub enum AnySurfaceMut<'a> {
591 Cells(&'a mut Surface<Cells>),
592 Pixels(&'a mut Surface<Pixels>),
593}
594
595impl AnySurface {
596 pub fn new(id: &str) -> Self {
598 let mut surface = Self {
599 id: id.into(),
600 surface: None,
601 };
602 surface.sync();
603 surface
604 }
605
606 pub fn id(&self) -> &str {
607 &self.id
608 }
609
610 pub fn model(&self) -> Model {
612 match self.surface {
613 Some(AnySurfaceInner::Pixels(_)) => Model::Pixels,
614 _ => Model::Cells,
615 }
616 }
617
618 fn current_model(&self) -> Option<Model> {
619 let inner = match self.surface.as_ref()? {
620 AnySurfaceInner::Cells(surface) => &surface.inner,
621 AnySurfaceInner::Pixels(surface) => &surface.inner,
622 };
623 let inner = inner.borrow();
624 match &inner.error {
625 Some(Error::WrongModel { found, .. }) => Some(*found),
626 _ => Some(inner.model),
627 }
628 }
629
630 pub fn sync(&mut self) -> bool {
633 let wanted = match self.current_model() {
634 Some(model) => model,
635 None => host::surface_layout(&self.id).map_or(Model::Cells, |layout| layout.model),
636 };
637 if self.surface.is_some() && Some(wanted) == self.surface.as_ref().map(|_| self.model()) {
638 return false;
639 }
640 self.surface = None;
643 self.surface = Some(match wanted {
644 Model::Cells => AnySurfaceInner::Cells(Surface::new(&self.id)),
645 Model::Pixels => AnySurfaceInner::Pixels(Surface::new(&self.id)),
646 });
647 true
648 }
649
650 pub fn get(&mut self) -> AnySurfaceMut<'_> {
652 self.sync();
653 match self.surface.as_mut() {
654 Some(AnySurfaceInner::Pixels(surface)) => AnySurfaceMut::Pixels(surface),
655 Some(AnySurfaceInner::Cells(surface)) => AnySurfaceMut::Cells(surface),
656 None => unreachable!("sync always leaves a surface"),
657 }
658 }
659}
660
661#[derive(Debug)]
668pub struct Instances<M: SurfaceModel> {
669 surface: String,
670 panes: alloc::collections::BTreeMap<String, Surface<M>>,
671}
672
673pub fn instance_pane<'a>(surface: &str, id: &'a str) -> Option<&'a str> {
675 id.strip_prefix(surface)?.strip_prefix('@')
676}
677
678impl<M: SurfaceModel> Instances<M> {
679 pub fn new(surface: &str) -> Self {
681 Self {
682 surface: surface.into(),
683 panes: alloc::collections::BTreeMap::new(),
684 }
685 }
686
687 pub fn event(&mut self, event: &crate::Event) -> Option<String> {
690 let (id, removed) = match event {
691 crate::Event::Resize { surface, geometry } => (surface, geometry.is_empty()),
692 crate::Event::Visibility { surface, .. } => (surface, false),
693 _ => return None,
694 };
695 let pane = String::from(instance_pane(&self.surface, id)?);
696 if removed {
697 self.panes.remove(&pane);
698 } else if !self.panes.contains_key(&pane) {
699 self.panes.insert(pane.clone(), Surface::new(id));
700 }
701 Some(pane)
702 }
703
704 pub fn get_mut(&mut self, pane: &str) -> Option<&mut Surface<M>> {
705 self.panes.get_mut(pane)
706 }
707
708 pub fn instance(&mut self, owner: &str) -> &mut Surface<M> {
713 let surface = &self.surface;
714 self.panes
715 .entry(String::from(owner))
716 .or_insert_with(|| Surface::new(&alloc::format!("{surface}@{owner}")))
717 }
718
719 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Surface<M>)> {
721 self.panes
722 .iter_mut()
723 .map(|(pane, surface)| (pane.as_str(), surface))
724 }
725
726 pub fn len(&self) -> usize {
727 self.panes.len()
728 }
729
730 pub fn is_empty(&self) -> bool {
731 self.panes.is_empty()
732 }
733}