use std::cell::{RefCell, RefMut};
use std::rc::Rc;
use std::sync::Arc;
use cosmic_text::FontSystem;
use crate::audio::AudioStore;
use crate::resources::{FontId, FragmentId, ImageId, Resources, SessionId, SoundId};
use crate::tree::OriginId;
use crate::window::{WindowConfig, WindowId};
pub(crate) struct SessionState {
pub(crate) id: SessionId,
pub(crate) fonts: FontSystem,
pub(crate) system: std::sync::Arc<crate::text::SystemFonts>,
pub(crate) system_fonts_rev: u64,
pub(crate) resources: Resources,
pub(crate) audio: AudioStore,
pub(crate) fonts_rev: u64,
pub(crate) weights_rev: u64,
pub(crate) faces_shared: bool,
pub(crate) images_rev: u64,
pub(crate) dropped: Dropped,
pub(crate) windows: WindowRegistry,
pub(crate) devtools: crate::runtime::devtools::State,
}
impl SessionState {
fn new() -> Self {
let id = SessionId::next();
let (fonts, system) = crate::text::new_font_system();
Self {
id,
fonts,
system,
system_fonts_rev: 0,
resources: Resources::new(id),
audio: AudioStore::default(),
fonts_rev: 0,
weights_rev: 0,
faces_shared: false,
images_rev: 0,
dropped: Dropped::default(),
windows: WindowRegistry::new(),
devtools: crate::runtime::devtools::State::default(),
}
}
}
impl SessionState {
pub(crate) fn remove_image(&mut self, id: ImageId) -> Option<crate::resources::ImageEntry> {
let entry = self.resources.remove_image(id)?;
self.images_rev += 1;
if entry.backing == crate::resources::ImageBacking::Texture {
self.dropped.images.push(id);
}
Some(entry)
}
pub(crate) fn remove_fragment(&mut self, id: FragmentId) -> bool {
if self.resources.remove_fragment(id).is_none() {
return false;
}
self.dropped.fragments.push(id);
true
}
}
#[derive(Default)]
pub(crate) struct Dropped {
pub(crate) images: Vec<ImageId>,
pub(crate) fragments: Vec<FragmentId>,
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct WindowDecl {
pub(crate) name: Rc<str>,
pub(crate) config: WindowConfig,
pub(crate) origin: OriginId,
pub(crate) conflict: bool,
}
struct WindowEntry {
name: Rc<str>,
id: WindowId,
live: bool,
}
pub(crate) enum WindowChange {
Opened {
id: WindowId,
name: Rc<str>,
owner: WindowId,
origin: OriginId,
config: WindowConfig,
conflict: bool,
},
Closed {
id: WindowId,
name: Rc<str>,
},
}
pub(crate) struct WindowRegistry {
slots: Vec<(WindowId, Vec<WindowDecl>)>,
windows: Vec<WindowEntry>,
next_id: u32,
}
pub const MAIN_WINDOW_NAME: &str = "main";
impl WindowRegistry {
fn new() -> Self {
Self {
slots: Vec::new(),
windows: vec![WindowEntry {
name: Rc::from(MAIN_WINDOW_NAME),
id: WindowId::MAIN,
live: true,
}],
next_id: 1,
}
}
pub(crate) fn set_slot(&mut self, id: WindowId, decls: &[WindowDecl]) {
match self.slots.binary_search_by_key(&id, |(i, _)| *i) {
Ok(i) if decls.is_empty() => {
self.slots.remove(i);
}
Ok(i) => {
self.slots[i].1.clear();
self.slots[i].1.extend_from_slice(decls);
}
Err(_) if decls.is_empty() => {}
Err(i) => self.slots.insert(i, (id, decls.to_vec())),
}
}
fn remove_slot(&mut self, id: WindowId) {
if let Ok(i) = self.slots.binary_search_by_key(&id, |(i, _)| *i) {
self.slots.remove(i);
}
}
pub(crate) fn name_of(&self, id: WindowId) -> Option<Rc<str>> {
self.windows
.iter()
.find(|w| w.id == id)
.map(|w| w.name.clone())
}
pub(crate) fn live(&self) -> Vec<(WindowId, Rc<str>)> {
self.windows
.iter()
.filter(|w| w.live)
.map(|w| (w.id, w.name.clone()))
.collect()
}
pub(crate) fn any_closed(&self) -> bool {
self.windows.iter().any(|w| !w.live)
}
pub(crate) fn closed_among<'a>(
&'a self,
decls: &'a [WindowDecl],
) -> impl Iterator<Item = Rc<str>> + 'a {
decls.iter().filter_map(|d| {
self.windows
.iter()
.find(|w| !w.live && w.name == d.name)
.map(|w| w.name.clone())
})
}
pub(crate) fn os_closed(&mut self, id: WindowId) -> Option<Rc<str>> {
if id == WindowId::MAIN {
return None;
}
let w = self.windows.iter_mut().find(|w| w.id == id && w.live)?;
w.live = false;
let name = w.name.clone();
self.remove_slot(id);
Some(name)
}
pub(crate) fn diff(&mut self, out: &mut Vec<WindowChange>) {
loop {
let union = self.union();
let gone: Vec<usize> = (0..self.windows.len())
.rev()
.filter(|&i| {
let w = &self.windows[i];
w.id != WindowId::MAIN && !union.iter().any(|u| u.0 == w.name)
})
.collect();
if gone.is_empty() {
for (name, config, owner, origin, conflict) in union {
if self.windows.iter().any(|w| w.name == name) {
continue;
}
let id = WindowId(self.next_id);
self.next_id += 1;
self.windows.push(WindowEntry {
name: name.clone(),
id,
live: true,
});
out.push(WindowChange::Opened {
id,
name,
owner,
origin,
config,
conflict,
});
}
return;
}
for i in gone {
let w = self.windows.remove(i);
self.remove_slot(w.id);
if w.live {
out.push(WindowChange::Closed {
id: w.id,
name: w.name,
});
}
}
}
}
fn union(&self) -> Vec<(Rc<str>, WindowConfig, WindowId, OriginId, bool)> {
let mut union: Vec<(Rc<str>, WindowConfig, WindowId, OriginId, bool)> = Vec::new();
for (slot, decls) in &self.slots {
for d in decls {
match union.iter_mut().find(|u| u.0 == d.name) {
Some(u) => u.4 |= u.1 != d.config,
None => union.push((d.name.clone(), d.config, *slot, d.origin, d.conflict)),
}
}
}
union
}
}
#[derive(Clone)]
pub struct Session(Rc<RefCell<SessionState>>);
impl Session {
pub fn new() -> Self {
Self(Rc::new(RefCell::new(SessionState::new())))
}
pub fn is(&self, other: &Session) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
pub fn id(&self) -> SessionId {
self.0.borrow().id
}
pub(crate) fn state(&self) -> RefMut<'_, SessionState> {
self.0.borrow_mut()
}
pub(crate) fn try_state(&self) -> Option<RefMut<'_, SessionState>> {
self.0.try_borrow_mut().ok()
}
}
impl Default for Session {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for Session {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Session")
}
}
#[derive(Clone)]
pub struct SharedAudio(Session);
impl SharedAudio {
pub(crate) fn new(session: &Session) -> Self {
Self(session.clone())
}
pub fn take_commands(&self) -> Vec<crate::audio::AudioCommand> {
self.0.state().audio.take_commands()
}
pub fn playback_of(
&self,
window: WindowId,
key: crate::key::Key,
) -> Option<crate::audio::PlaybackId> {
self.0.state().audio.playback_of(window, key)
}
#[cfg(feature = "conformance")]
pub(crate) fn any_mounted(&self, window: WindowId) -> bool {
self.0.state().audio.any_mounted(window)
}
}
#[derive(Clone)]
pub struct SharedResources(Session);
impl SharedResources {
pub(crate) fn new(session: &Session) -> Self {
Self(session.clone())
}
pub fn add_image(&self, width: u32, height: u32, rgba: Vec<u8>) -> ImageId {
self.0.state().resources.add_image(width, height, rgba)
}
pub fn remove_image(&self, id: ImageId) -> bool {
self.0.state().remove_image(id).is_some()
}
pub fn image_size(&self, id: ImageId) -> Option<(u32, u32)> {
let state = self.0.state();
let e = state.resources.image(id)?;
Some((e.width, e.height))
}
pub fn add_sound(&self, bytes: Vec<u8>) -> SoundId {
self.0.state().resources.add_sound(bytes)
}
pub fn has_sounds(&self) -> bool {
self.0.state().resources.has_sounds()
}
pub fn sound(&self, id: SoundId) -> Option<Arc<[u8]>> {
self.0.state().resources.sound(id).cloned()
}
pub fn font_family(&self, id: FontId) -> Option<String> {
self.0.state().resources.font_family(id).map(str::to_owned)
}
}