use {
crate::{
context::{Context, FailedMakeContext},
el::{Loop, LoopError},
element::Element,
format::Format,
init,
state::{State, Target},
update::Update,
},
std::{
error, fmt,
future::{Future, IntoFuture},
marker::PhantomData,
ops,
pin::Pin,
sync::Arc,
task::{self, Poll},
},
wgpu::{
CreateSurfaceError, Instance, Surface, SurfaceConfiguration, SurfaceError, SurfaceTexture,
TextureView,
},
winit::{
error::{EventLoopError, OsError},
event_loop::{EventLoopClosed, EventLoopProxy},
window::{self, WindowId},
},
};
pub struct WindowBuilder<V> {
element: Option<Element>,
title: String,
size: Option<(u32, u32)>,
evty: PhantomData<V>,
}
impl<V> WindowBuilder<V> {
pub(crate) fn new(element: Element) -> Self {
Self {
element: Some(element),
title: String::default(),
size: Some((600, 600)),
evty: PhantomData,
}
}
pub fn with_title<S>(mut self, title: S) -> Self
where
S: Into<String>,
{
self.title = title.into();
self
}
pub fn with_size(mut self, size: (u32, u32)) -> Self {
self.size = Some(size);
self
}
pub fn with_fullscreen(mut self) -> Self {
self.size = None;
self
}
fn build(&mut self, cx: Context, instance: &Instance) -> Result<Window<V>, Error> {
use {
std::mem,
winit::{dpi::PhysicalSize, window::Fullscreen},
};
let lu = Loop::new()?;
let inner = {
let title = mem::take(&mut self.title);
let builder = window::WindowBuilder::new().with_title(title);
let builder = match self.size {
Some((width, height)) => builder.with_inner_size(PhysicalSize::new(width, height)),
None => builder.with_fullscreen(Some(Fullscreen::Borderless(None))),
};
Arc::new(builder.build(lu.inner())?)
};
let view = {
let el = self.element.take().expect("take the element once");
el.set_canvas(&inner);
el.set_window_size(&inner);
View::new(cx.state(), instance, inner, el)?
};
Ok(Window { cx, lu, view })
}
}
impl<V> IntoFuture for WindowBuilder<V>
where
V: 'static,
{
type Output = Result<Window<V>, Error>;
type IntoFuture = Build<V>;
fn into_future(mut self) -> Self::IntoFuture {
let fut = async move {
let (cx, instance) = init::make().await?;
self.build(cx, &instance)
};
Build(Box::pin(fut))
}
}
type BoxFuture<T> = Pin<Box<dyn Future<Output = T>>>;
pub struct Build<V>(BoxFuture<Result<Window<V>, Error>>)
where
V: 'static;
impl<V> Future for Build<V>
where
V: 'static,
{
type Output = Result<Window<V>, Error>;
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
self.get_mut().0.as_mut().poll(cx)
}
}
pub struct Window<V = ()>
where
V: 'static,
{
cx: Context,
lu: Loop<V>,
view: View,
}
impl<V> Window<V>
where
V: 'static,
{
pub fn context(&self) -> Context {
self.cx.clone()
}
pub fn notifier(&self) -> Notifier<V> {
Notifier(self.lu.inner().create_proxy())
}
#[cfg(not(target_arch = "wasm32"))]
pub fn run_local<U>(self, upd: U) -> Result<(), LoopError>
where
U: Update<Event = V>,
{
self.lu.run(self.cx, self.view, upd)
}
pub fn run<U>(self, upd: U) -> Result<(), LoopError>
where
U: Update<Event = V> + 'static,
{
#[cfg(not(target_arch = "wasm32"))]
{
self.lu.run(self.cx, self.view, upd)?;
}
#[cfg(target_arch = "wasm32")]
{
self.lu.spawn(self.cx, self.view, upd);
}
Ok(())
}
}
impl<V> ops::Deref for Window<V> {
type Target = View;
fn deref(&self) -> &Self::Target {
&self.view
}
}
type Inner = Arc<window::Window>;
pub struct Notifier<V>(EventLoopProxy<V>)
where
V: 'static;
impl<V> Notifier<V>
where
V: 'static,
{
pub fn notify(&self, ev: V) -> Result<(), V> {
self.0.send_event(ev).map_err(|EventLoopClosed(ev)| ev)
}
}
pub struct View {
conf: SurfaceConfiguration,
surface: Surface<'static>,
inner: Inner,
el: Element,
}
impl View {
fn new(state: &State, instance: &Instance, inner: Inner, el: Element) -> Result<Self, Error> {
use wgpu::*;
const SUPPORTED_FORMATS: [Format; 4] = [
Format::SrgbAlpha,
Format::SbgrAlpha,
Format::RgbAlpha,
Format::BgrAlpha,
];
let surface = instance.create_surface(Arc::clone(&inner))?;
let conf = {
let caps = surface.get_capabilities(state.adapter());
let format = SUPPORTED_FORMATS.into_iter().find_map(|format| {
let format = format.wgpu();
caps.formats.contains(&format).then_some(format)
});
let Some(format) = format else {
log::error!("surface formats: {formats:?}", formats = &caps.formats);
return Err(ErrorKind::UnsupportedSurface.into());
};
let size = inner.inner_size();
SurfaceConfiguration {
usage: TextureUsages::RENDER_ATTACHMENT,
format,
width: size.width.max(1),
height: size.height.max(1),
present_mode: PresentMode::default(),
desired_maximum_frame_latency: 2,
alpha_mode: CompositeAlphaMode::default(),
view_formats: vec![],
}
};
surface.configure(state.device(), &conf);
Ok(Self {
conf,
surface,
inner,
el,
})
}
pub fn window(&self) -> Inner {
Arc::clone(&self.inner)
}
pub fn format(&self) -> Format {
Format::from_wgpu(self.conf.format)
}
pub fn size(&self) -> (u32, u32) {
(self.conf.width, self.conf.height)
}
pub(crate) fn id(&self) -> WindowId {
self.inner.id()
}
pub(crate) fn request_redraw(&self) {
self.inner.request_redraw();
}
pub(crate) fn output(&self) -> Result<Output, SurfaceError> {
use wgpu::TextureViewDescriptor;
let output = self.surface.get_current_texture()?;
let view = {
let desc = TextureViewDescriptor::default();
output.texture.create_view(&desc)
};
Ok(Output {
view,
format: self.format(),
output,
})
}
pub(crate) fn set_window_size(&self) {
self.el.set_window_size(&self.inner);
}
pub(crate) fn resize(&mut self, state: &State) {
let size = self.inner.inner_size();
if size.width > 0 && size.height > 0 {
self.conf.width = size.width;
self.conf.height = size.height;
self.surface.configure(state.device(), &self.conf);
}
}
}
pub(crate) struct Output {
view: TextureView,
format: Format,
output: SurfaceTexture,
}
impl Output {
pub fn target(&self) -> Target {
Target::new(self.format, &self.view)
}
pub fn present(self) {
self.output.present();
}
}
#[derive(Debug)]
pub struct Error(ErrorKind);
impl From<ErrorKind> for Error {
fn from(v: ErrorKind) -> Self {
Self(v)
}
}
impl From<EventLoopError> for Error {
fn from(v: EventLoopError) -> Self {
Self(ErrorKind::EventLoop(v))
}
}
impl From<OsError> for Error {
fn from(v: OsError) -> Self {
Self(ErrorKind::Os(v))
}
}
impl From<CreateSurfaceError> for Error {
fn from(v: CreateSurfaceError) -> Self {
Self(ErrorKind::Surface(v))
}
}
impl From<FailedMakeContext> for Error {
fn from(v: FailedMakeContext) -> Self {
Self(ErrorKind::Context(v))
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.0 {
ErrorKind::UnsupportedSurface => write!(f, "unsupported surface"),
ErrorKind::EventLoop(err) => err.fmt(f),
ErrorKind::Os(err) => err.fmt(f),
ErrorKind::Surface(err) => err.fmt(f),
ErrorKind::Context(err) => err.fmt(f),
}
}
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match &self.0 {
ErrorKind::UnsupportedSurface => None,
ErrorKind::EventLoop(err) => Some(err),
ErrorKind::Os(err) => Some(err),
ErrorKind::Surface(err) => Some(err),
ErrorKind::Context(err) => Some(err),
}
}
}
#[derive(Debug)]
enum ErrorKind {
UnsupportedSurface,
EventLoop(EventLoopError),
Os(OsError),
Surface(CreateSurfaceError),
Context(FailedMakeContext),
}