use std::sync::atomic::{AtomicI32, Ordering};
use capi::sctypes::{LPVOID, LPCSTR};
pub use capi::scom::*;
pub fn atom(name: &str) -> som_atom_t {
let s = s2u!(name);
(crate::_API.SciterAtomValue)(s.as_ptr())
}
pub fn atom_name(id: som_atom_t) -> Option<String> {
let mut s = String::new();
let ok = (crate::_API.SciterAtomNameCB)(id, crate::utf::store_astr, &mut s as *mut _ as LPVOID);
if ok != 0 {
Some(s)
} else {
None
}
}
pub trait Passport {
fn get_passport(&self) -> &'static som_passport_t;
}
pub struct IAssetRef<T> {
asset: *mut som_asset_t,
ty: std::marker::PhantomData<T>,
}
impl<T> Clone for IAssetRef<T> {
fn clone(&self) -> Self {
self.add_ref();
Self {
asset: self.asset,
ty: self.ty,
}
}
}
impl<T> Drop for IAssetRef<T> {
fn drop(&mut self) {
self.release();
}
}
impl<T> std::fmt::Debug for IAssetRef<T> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.add_ref();
let rc = self.release();
let name = self::atom_name(self.get_passport().name);
write!(f, "Asset({}):{}", name.unwrap_or_default(), rc)
}
}
impl<T> From<Box<IAsset<T>>> for IAssetRef<T> {
fn from(asset: Box<IAsset<T>>) -> Self {
Self::from_raw(IAsset::into_raw(asset))
}
}
impl<T> IAssetRef<T> {
fn isa(&self) -> &'static som_asset_class_t {
unsafe { (*self.asset).isa }
}
fn add_ref(&self) -> i32 {
(self.isa().add_ref)(self.asset)
}
fn release(&self) -> i32 {
(self.isa().release)(self.asset)
}
}
impl<T> IAssetRef<T> {
pub fn from_raw(asset: *mut som_asset_t) -> Self {
eprintln!("IAssetRef<{}>::from({:?})", std::any::type_name::<T>(), asset);
assert!(!asset.is_null());
let me = Self {
asset,
ty: std::marker::PhantomData,
};
me.add_ref();
me
}
pub fn into_raw(asset: IAssetRef<T>) -> *mut som_asset_t {
asset.release();
let ptr = asset.asset;
std::mem::forget(asset);
ptr
}
pub fn as_ptr(&self) -> *mut som_asset_t {
self.asset
}
pub fn as_asset(&self) -> &som_asset_t {
unsafe { & *self.asset }
}
pub fn get_passport(&self) -> &som_passport_t {
let ptr = (self.isa().get_passport)(self.asset);
unsafe { & *ptr }
}
}
#[repr(C)]
pub struct IAsset<T> {
asset: som_asset_t,
refc: AtomicI32,
passport: Option<&'static som_passport_t>,
data: T,
}
pub fn set_global<T>(asset: IAssetRef<T>) {
let ptr = asset.as_ptr();
(crate::_API.SciterSetGlobalAsset)(ptr);
}
pub fn into_global<T>(asset: Box<IAsset<T>>) {
let ptr = IAsset::into_raw(asset);
(crate::_API.SciterSetGlobalAsset)(ptr);
}
impl<T> std::ops::Deref for IAsset<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<T> std::ops::DerefMut for IAsset<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.data
}
}
impl<T> Drop for IAsset<T> {
fn drop(&mut self) {
let rc = self.refc.load(Ordering::SeqCst);
if rc != 0 {
eprintln!("asset<{}>::drop with {} references alive", std::any::type_name::<T>(), rc);
}
assert_eq!(rc, 0);
let ptr = self.asset.isa as *const som_asset_class_t;
let ptr = unsafe { Box::from_raw(ptr as *mut som_asset_class_t) };
drop(ptr);
}
}
impl<T> IAsset<T> {
#[allow(clippy::mut_from_ref)]
pub fn from_raw(thing: &*mut som_asset_t) -> &mut IAsset<T> {
assert!(!thing.is_null());
unsafe { &mut *(*thing as *mut IAsset<T>) }
}
fn into_raw(asset: Box<IAsset<T>>) -> *mut som_asset_t {
let p = Box::into_raw(asset);
p as *mut som_asset_t
}
}
impl<T: Passport> IAsset<T> {
pub fn new(data: T) -> Box<Self> {
let isa = Box::new(Self::class());
let me = Self {
asset: som_asset_t { isa: Box::leak(isa) },
refc: Default::default(),
passport: None,
data,
};
Box::new(me)
}
fn class() -> som_asset_class_t {
extern "C" fn asset_add_ref<T>(thing: *mut som_asset_t) -> i32 {
{
let me = IAsset::<T>::from_raw(&thing);
let t = me.refc.fetch_add(1, Ordering::SeqCst) + 1;
return t;
}
}
extern "C" fn asset_release<T>(thing: *mut som_asset_t) -> i32 {
let t = {
let me = IAsset::<T>::from_raw(&thing);
me.refc.fetch_sub(1, Ordering::SeqCst) - 1
};
if t == 0 {
let me = unsafe { Box::from_raw(thing as *mut IAsset<T>) };
drop(me);
}
return t;
}
extern "C" fn asset_get_interface<T>(_thing: *mut som_asset_t, name: LPCSTR, _out: *mut *mut som_asset_t) -> bool {
let name = u2s!(name);
eprintln!("iasset<T>::get_interface({}) is not implemented.", name);
return false;
}
extern "C" fn asset_get_passport<T: Passport>(thing: *mut som_asset_t) -> *const som_passport_t
{
let me = IAsset::<T>::from_raw(&thing);
if me.passport.is_none() {
me.passport = Some(me.data.get_passport());
}
let ps = me.passport.as_ref().unwrap();
return *ps;
}
som_asset_class_t {
add_ref: asset_add_ref::<T>,
release: asset_release::<T>,
get_interface: asset_get_interface::<T>,
get_passport: asset_get_passport::<T>,
}
}
}