use std::sync::Mutex;
use gio::glib;
use glib::prelude::*;
use glib::subclass::prelude::*;
use glycin_common::MemoryFormatSelection;
use super::GlyImage;
use crate::error::ResultExt;
use crate::{Error, GInputStreamSend, SandboxSelector, Source};
static_assertions::assert_impl_all!(GlyLoader: Send, Sync);
use super::init;
pub mod imp {
use super::*;
#[derive(Default, Debug, glib::Properties)]
#[properties(wrapper_type = super::GlyLoader)]
pub struct GlyLoader {
#[property(get, construct_only)]
pub(super) file: Mutex<Option<gio::File>>,
#[property(get=Self::stream, set=Self::set_stream, construct_only, type=Option<gio::InputStream>)]
pub(super) stream: Mutex<Option<GInputStreamSend>>,
#[property(get, construct_only)]
pub(super) bytes: Mutex<Option<glib::Bytes>>,
#[property(get, set)]
cancellable: Mutex<gio::Cancellable>,
#[property(get, set, builder(SandboxSelector::default()))]
sandbox_selector: Mutex<SandboxSelector>,
#[property(get, set)]
memory_format_selection: Mutex<MemoryFormatSelection>,
#[property(get, set)]
apply_transformation: Mutex<bool>,
}
#[glib::object_subclass]
impl ObjectSubclass for GlyLoader {
const NAME: &'static str = "GlyLoader";
type Type = super::GlyLoader;
}
#[glib::derived_properties]
impl ObjectImpl for GlyLoader {
fn constructed(&self) {
self.parent_constructed();
init();
let obj = self.obj();
*self.apply_transformation.lock().unwrap() = true;
if obj.file().is_some() as u8
+ obj.stream().is_some() as u8
+ obj.bytes().is_some() as u8
!= 1
{
glib::g_critical!(
"glycin",
"A loader needs to be initialized with exactly one of 'file', 'stream', or 'bytes'."
);
}
}
}
impl GlyLoader {
fn stream(&self) -> Option<gio::InputStream> {
self.stream.lock().unwrap().as_ref().map(|x| x.stream())
}
fn set_stream(&self, stream: Option<&gio::InputStream>) {
let stream = unsafe { stream.map(|x| GInputStreamSend::new(x.clone())) };
*self.stream.lock().unwrap() = stream;
}
}
}
glib::wrapper! {
pub struct GlyLoader(ObjectSubclass<imp::GlyLoader>);
}
impl GlyLoader {
pub fn new(file: &gio::File) -> Self {
glib::Object::builder().property("file", file).build()
}
pub fn for_stream(stream: &gio::InputStream) -> Self {
glib::Object::builder().property("stream", stream).build()
}
pub fn for_bytes(bytes: &glib::Bytes) -> Self {
glib::Object::builder().property("bytes", bytes).build()
}
pub async fn load(&self) -> Result<GlyImage, crate::ErrorCtx> {
let mut loader = if let Some(file) = std::mem::take(&mut *self.imp().file.lock().unwrap()) {
crate::Loader::new(file)
} else if let Some(stream) = std::mem::take(&mut *self.imp().stream.lock().unwrap()) {
crate::Loader::new_source(Source::Stream(stream))
} else if let Some(bytes) = std::mem::take(&mut *self.imp().bytes.lock().unwrap()) {
crate::Loader::new_bytes(bytes)
} else {
return Err(Error::LoaderUsedTwice).err_no_context(&self.cancellable());
};
loader.sandbox_selector = self.sandbox_selector();
loader.memory_format_selection = self.memory_format_selection();
loader.apply_transformations = self.apply_transformation();
loader.cancellable(self.cancellable());
let image = loader.load().await?;
Ok(GlyImage::new(image))
}
}