use crate::color::IntoLinSrgba;
use crate::event::{
Key, MouseButton, MouseScrollDelta, TouchEvent, TouchPhase, TouchpadPressure, WindowEvent,
};
use crate::frame::{self, Frame, RawFrame};
use crate::geom;
use crate::geom::Point2;
use crate::glam::Vec2;
use crate::wgpu;
use crate::App;
use std::any::Any;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Duration;
use std::{env, fmt};
use wgpu_upstream::CompositeAlphaMode;
use winit::dpi::{LogicalSize, PhysicalSize};
#[cfg(target_os = "macos")]
use winit::platform::macos::WindowBuilderExtMacOS;
pub use winit::window::Fullscreen;
pub use winit::window::WindowId as Id;
use winit::window::{CursorGrabMode, WindowLevel};
pub const DEFAULT_DIMENSIONS: LogicalSize<geom::scalar::Default> = LogicalSize {
width: 1024.0,
height: 768.0,
};
pub const MIN_SC_PIXELS: PhysicalSize<u32> = PhysicalSize {
width: 2,
height: 2,
};
pub struct Builder<'app> {
app: &'app App,
window: winit::window::WindowBuilder,
title_was_set: bool,
surface_conf_builder: SurfaceConfigurationBuilder,
power_preference: wgpu::PowerPreference,
force_fallback_adapter: bool,
device_desc: Option<wgpu::DeviceDescriptor<'static>>,
user_functions: UserFunctions,
msaa_samples: Option<u32>,
max_capture_frame_jobs: u32,
capture_frame_timeout: Option<Duration>,
clear_color: Option<wgpu::Color>,
}
#[derive(Debug, Default)]
pub(crate) struct UserFunctions {
pub(crate) view: Option<View>,
pub(crate) event: Option<EventFnAny>,
pub(crate) raw_event: Option<RawEventFnAny>,
pub(crate) key_pressed: Option<KeyPressedFnAny>,
pub(crate) key_released: Option<KeyReleasedFnAny>,
pub(crate) received_character: Option<ReceivedCharacterFnAny>,
pub(crate) mouse_moved: Option<MouseMovedFnAny>,
pub(crate) mouse_pressed: Option<MousePressedFnAny>,
pub(crate) mouse_released: Option<MouseReleasedFnAny>,
pub(crate) mouse_entered: Option<MouseEnteredFnAny>,
pub(crate) mouse_exited: Option<MouseExitedFnAny>,
pub(crate) mouse_wheel: Option<MouseWheelFnAny>,
pub(crate) moved: Option<MovedFnAny>,
pub(crate) resized: Option<ResizedFnAny>,
pub(crate) touch: Option<TouchFnAny>,
pub(crate) touchpad_pressure: Option<TouchpadPressureFnAny>,
pub(crate) hovered_file: Option<HoveredFileFnAny>,
pub(crate) hovered_file_cancelled: Option<HoveredFileCancelledFnAny>,
pub(crate) dropped_file: Option<DroppedFileFnAny>,
pub(crate) focused: Option<FocusedFnAny>,
pub(crate) unfocused: Option<UnfocusedFnAny>,
pub(crate) closed: Option<ClosedFnAny>,
}
pub type ViewFn<Model> = fn(&App, &Model, Frame);
pub type RawViewFn<Model> = fn(&App, &Model, RawFrame);
pub type SketchFn = fn(&App, Frame);
#[derive(Clone)]
pub(crate) enum View {
WithModel(ViewFnAny),
WithModelRaw(RawViewFnAny),
Sketch(SketchFn),
}
pub type RawEventFn<Model> = fn(&App, &mut Model, &winit::event::WindowEvent);
pub type EventFn<Model> = fn(&App, &mut Model, WindowEvent);
pub type KeyPressedFn<Model> = fn(&App, &mut Model, Key);
pub type KeyReleasedFn<Model> = fn(&App, &mut Model, Key);
pub type ReceivedCharacterFn<Model> = fn(&App, &mut Model, char);
pub type MouseMovedFn<Model> = fn(&App, &mut Model, Point2);
pub type MousePressedFn<Model> = fn(&App, &mut Model, MouseButton);
pub type MouseReleasedFn<Model> = fn(&App, &mut Model, MouseButton);
pub type MouseEnteredFn<Model> = fn(&App, &mut Model);
pub type MouseExitedFn<Model> = fn(&App, &mut Model);
pub type MouseWheelFn<Model> = fn(&App, &mut Model, MouseScrollDelta, TouchPhase);
pub type MovedFn<Model> = fn(&App, &mut Model, Vec2);
pub type ResizedFn<Model> = fn(&App, &mut Model, Vec2);
pub type TouchFn<Model> = fn(&App, &mut Model, TouchEvent);
pub type TouchpadPressureFn<Model> = fn(&App, &mut Model, TouchpadPressure);
pub type HoveredFileFn<Model> = fn(&App, &mut Model, PathBuf);
pub type HoveredFileCancelledFn<Model> = fn(&App, &mut Model);
pub type DroppedFileFn<Model> = fn(&App, &mut Model, PathBuf);
pub type FocusedFn<Model> = fn(&App, &mut Model);
pub type UnfocusedFn<Model> = fn(&App, &mut Model);
pub type ClosedFn<Model> = fn(&App, &mut Model);
#[derive(Debug)]
pub enum BuildError {
NoAvailableAdapter,
WinitOsError(winit::error::OsError),
}
macro_rules! fn_any {
($TFn:ident<M>, $TFnAny:ident) => {
#[derive(Clone)]
pub(crate) struct $TFnAny {
fn_ptr: Arc<dyn Any>,
}
impl $TFnAny {
pub fn from_fn_ptr<M>(fn_ptr: $TFn<M>) -> Self
where
M: 'static,
{
let fn_ptr = Arc::new(fn_ptr) as Arc<dyn Any>;
$TFnAny { fn_ptr }
}
pub fn to_fn_ptr<M>(&self) -> Option<&$TFn<M>>
where
M: 'static,
{
self.fn_ptr.downcast_ref::<$TFn<M>>()
}
}
impl fmt::Debug for $TFnAny {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", stringify!($TFnAny))
}
}
};
}
fn_any!(ViewFn<M>, ViewFnAny);
fn_any!(RawViewFn<M>, RawViewFnAny);
fn_any!(EventFn<M>, EventFnAny);
fn_any!(RawEventFn<M>, RawEventFnAny);
fn_any!(KeyPressedFn<M>, KeyPressedFnAny);
fn_any!(KeyReleasedFn<M>, KeyReleasedFnAny);
fn_any!(ReceivedCharacterFn<M>, ReceivedCharacterFnAny);
fn_any!(MouseMovedFn<M>, MouseMovedFnAny);
fn_any!(MousePressedFn<M>, MousePressedFnAny);
fn_any!(MouseReleasedFn<M>, MouseReleasedFnAny);
fn_any!(MouseEnteredFn<M>, MouseEnteredFnAny);
fn_any!(MouseExitedFn<M>, MouseExitedFnAny);
fn_any!(MouseWheelFn<M>, MouseWheelFnAny);
fn_any!(MovedFn<M>, MovedFnAny);
fn_any!(ResizedFn<M>, ResizedFnAny);
fn_any!(TouchFn<M>, TouchFnAny);
fn_any!(TouchpadPressureFn<M>, TouchpadPressureFnAny);
fn_any!(HoveredFileFn<M>, HoveredFileFnAny);
fn_any!(HoveredFileCancelledFn<M>, HoveredFileCancelledFnAny);
fn_any!(DroppedFileFn<M>, DroppedFileFnAny);
fn_any!(FocusedFn<M>, FocusedFnAny);
fn_any!(UnfocusedFn<M>, UnfocusedFnAny);
fn_any!(ClosedFn<M>, ClosedFnAny);
#[derive(Debug)]
pub struct Window {
pub(crate) window: winit::window::Window,
pub(crate) surface: wgpu::Surface,
pub(crate) surface_conf: wgpu::SurfaceConfiguration,
pub(crate) device_queue_pair: Arc<wgpu::DeviceQueuePair>,
msaa_samples: u32,
pub(crate) frame_data: Option<FrameData>,
pub(crate) frame_count: u64,
pub(crate) user_functions: UserFunctions,
pub(crate) tracked_state: TrackedState,
pub(crate) is_invalidated: bool, pub(crate) clear_color: wgpu::Color,
}
#[derive(Debug)]
pub(crate) struct FrameData {
pub(crate) render: frame::RenderData,
pub(crate) capture: frame::CaptureData,
}
#[derive(Debug)]
pub(crate) struct TrackedState {
pub(crate) scale_factor: f64,
pub(crate) physical_size: winit::dpi::PhysicalSize<u32>,
}
#[derive(Clone, Debug, Default)]
pub struct SurfaceConfigurationBuilder {
pub usage: Option<wgpu::TextureUsages>,
pub format: Option<wgpu::TextureFormat>,
pub present_mode: Option<wgpu::PresentMode>,
}
impl SurfaceConfigurationBuilder {
pub const DEFAULT_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb;
pub const DEFAULT_PRESENT_MODE: wgpu::PresentMode = wgpu::PresentMode::Fifo;
pub const DEFAULT_USAGE: wgpu::TextureUsages = wgpu::TextureUsages::RENDER_ATTACHMENT;
pub fn new() -> Self {
Default::default()
}
pub fn from_configuration(conf: &wgpu::SurfaceConfiguration) -> Self {
SurfaceConfigurationBuilder::new()
.usage(conf.usage)
.format(conf.format)
.present_mode(conf.present_mode)
}
pub fn usage(mut self, usage: wgpu::TextureUsages) -> Self {
self.usage = Some(usage);
self
}
pub fn format(mut self, format: wgpu::TextureFormat) -> Self {
self.format = Some(format);
self
}
pub fn present_mode(mut self, present_mode: wgpu::PresentMode) -> Self {
self.present_mode = Some(present_mode);
self
}
pub(crate) fn build(
self,
surface: &wgpu::Surface,
adapter: &wgpu::Adapter,
[width_px, height_px]: [u32; 2],
) -> wgpu::SurfaceConfiguration {
let usage = self.usage.unwrap_or(Self::DEFAULT_USAGE);
let format = self
.format
.or_else(|| {
surface
.get_capabilities(&adapter)
.formats
.get(0)
.map(|x| x.clone())
})
.unwrap_or(Self::DEFAULT_FORMAT);
let present_mode = self.present_mode.unwrap_or(Self::DEFAULT_PRESENT_MODE);
wgpu::SurfaceConfiguration {
usage,
format,
width: width_px,
height: height_px,
present_mode,
alpha_mode: CompositeAlphaMode::Auto,
view_formats: Vec::new(),
}
}
}
impl<'app> Builder<'app> {
pub const DEFAULT_POWER_PREFERENCE: wgpu::PowerPreference = wgpu::DEFAULT_POWER_PREFERENCE;
pub const DEFAULT_FORCE_FALLBACK_ADAPTER: bool = false;
pub fn new(app: &'app App) -> Self {
Builder {
app,
window: winit::window::WindowBuilder::new(),
title_was_set: false,
surface_conf_builder: Default::default(),
power_preference: Self::DEFAULT_POWER_PREFERENCE,
force_fallback_adapter: Self::DEFAULT_FORCE_FALLBACK_ADAPTER,
device_desc: None,
user_functions: Default::default(),
msaa_samples: None,
max_capture_frame_jobs: Default::default(),
capture_frame_timeout: Default::default(),
clear_color: None,
}
}
pub fn window(mut self, window: winit::window::WindowBuilder) -> Self {
self.window = window;
self
}
pub fn surface_conf_builder(
mut self,
surface_conf_builder: SurfaceConfigurationBuilder,
) -> Self {
self.surface_conf_builder = surface_conf_builder;
self
}
pub fn power_preference(mut self, pref: wgpu::PowerPreference) -> Self {
self.power_preference = pref;
self
}
pub fn force_fallback_adapter(mut self, force: bool) -> Self {
self.force_fallback_adapter = force;
self
}
pub fn device_descriptor(mut self, device_desc: wgpu::DeviceDescriptor<'static>) -> Self {
self.device_desc = Some(device_desc);
self
}
pub fn msaa_samples(mut self, msaa_samples: u32) -> Self {
self.msaa_samples = Some(msaa_samples);
self
}
pub fn sketch(mut self, sketch_fn: SketchFn) -> Self {
self.user_functions.view = Some(View::Sketch(sketch_fn));
self
}
pub fn view<M>(mut self, view_fn: ViewFn<M>) -> Self
where
M: 'static,
{
self.user_functions.view = Some(View::WithModel(ViewFnAny::from_fn_ptr(view_fn)));
self
}
pub fn raw_view<M>(mut self, raw_view_fn: RawViewFn<M>) -> Self
where
M: 'static,
{
self.user_functions.view = Some(View::WithModelRaw(RawViewFnAny::from_fn_ptr(raw_view_fn)));
self
}
pub fn clear_color<C>(mut self, color: C) -> Self
where
C: IntoLinSrgba<f32>,
{
let lin_srgba = color.into_lin_srgba();
let (r, g, b, a) = lin_srgba.into_components();
let (r, g, b, a) = (r as f64, g as f64, b as f64, a as f64);
self.clear_color = Some(wgpu::Color { r, g, b, a });
self
}
pub fn event<M>(mut self, event_fn: EventFn<M>) -> Self
where
M: 'static,
{
self.user_functions.event = Some(EventFnAny::from_fn_ptr(event_fn));
self
}
pub fn raw_event<M>(mut self, raw_event_fn: RawEventFn<M>) -> Self
where
M: 'static,
{
self.user_functions.raw_event = Some(RawEventFnAny::from_fn_ptr(raw_event_fn));
self
}
pub fn key_pressed<M>(mut self, f: KeyPressedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.key_pressed = Some(KeyPressedFnAny::from_fn_ptr(f));
self
}
pub fn key_released<M>(mut self, f: KeyReleasedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.key_released = Some(KeyReleasedFnAny::from_fn_ptr(f));
self
}
pub fn received_character<M>(mut self, f: ReceivedCharacterFn<M>) -> Self
where
M: 'static,
{
self.user_functions.received_character = Some(ReceivedCharacterFnAny::from_fn_ptr(f));
self
}
pub fn mouse_moved<M>(mut self, f: MouseMovedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.mouse_moved = Some(MouseMovedFnAny::from_fn_ptr(f));
self
}
pub fn mouse_pressed<M>(mut self, f: MousePressedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.mouse_pressed = Some(MousePressedFnAny::from_fn_ptr(f));
self
}
pub fn mouse_released<M>(mut self, f: MouseReleasedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.mouse_released = Some(MouseReleasedFnAny::from_fn_ptr(f));
self
}
pub fn mouse_wheel<M>(mut self, f: MouseWheelFn<M>) -> Self
where
M: 'static,
{
self.user_functions.mouse_wheel = Some(MouseWheelFnAny::from_fn_ptr(f));
self
}
pub fn mouse_entered<M>(mut self, f: MouseEnteredFn<M>) -> Self
where
M: 'static,
{
self.user_functions.mouse_entered = Some(MouseEnteredFnAny::from_fn_ptr(f));
self
}
pub fn mouse_exited<M>(mut self, f: MouseExitedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.mouse_exited = Some(MouseExitedFnAny::from_fn_ptr(f));
self
}
pub fn touch<M>(mut self, f: TouchFn<M>) -> Self
where
M: 'static,
{
self.user_functions.touch = Some(TouchFnAny::from_fn_ptr(f));
self
}
pub fn touchpad_pressure<M>(mut self, f: TouchpadPressureFn<M>) -> Self
where
M: 'static,
{
self.user_functions.touchpad_pressure = Some(TouchpadPressureFnAny::from_fn_ptr(f));
self
}
pub fn moved<M>(mut self, f: MovedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.moved = Some(MovedFnAny::from_fn_ptr(f));
self
}
pub fn resized<M>(mut self, f: ResizedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.resized = Some(ResizedFnAny::from_fn_ptr(f));
self
}
pub fn hovered_file<M>(mut self, f: HoveredFileFn<M>) -> Self
where
M: 'static,
{
self.user_functions.hovered_file = Some(HoveredFileFnAny::from_fn_ptr(f));
self
}
pub fn hovered_file_cancelled<M>(mut self, f: HoveredFileCancelledFn<M>) -> Self
where
M: 'static,
{
self.user_functions.hovered_file_cancelled =
Some(HoveredFileCancelledFnAny::from_fn_ptr(f));
self
}
pub fn dropped_file<M>(mut self, f: DroppedFileFn<M>) -> Self
where
M: 'static,
{
self.user_functions.dropped_file = Some(DroppedFileFnAny::from_fn_ptr(f));
self
}
pub fn focused<M>(mut self, f: FocusedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.focused = Some(FocusedFnAny::from_fn_ptr(f));
self
}
pub fn unfocused<M>(mut self, f: UnfocusedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.unfocused = Some(UnfocusedFnAny::from_fn_ptr(f));
self
}
pub fn closed<M>(mut self, f: ClosedFn<M>) -> Self
where
M: 'static,
{
self.user_functions.closed = Some(ClosedFnAny::from_fn_ptr(f));
self
}
pub fn max_capture_frame_jobs(mut self, max_jobs: u32) -> Self {
assert!(
max_jobs >= 1,
"must allow for at least one capture frame job at a time"
);
self.max_capture_frame_jobs = max_jobs;
self
}
pub fn capture_frame_timeout(mut self, timeout: Option<std::time::Duration>) -> Self {
self.capture_frame_timeout = timeout;
self
}
#[cfg(not(target_os = "unknown"))]
pub fn build(self) -> Result<Id, BuildError> {
tokio::task::block_in_place(move || {
tokio::runtime::Handle::current().block_on(async move { self.build_async().await })
})
}
pub async fn build_async(self) -> Result<Id, BuildError> {
let Builder {
app,
mut window,
title_was_set,
surface_conf_builder,
power_preference,
force_fallback_adapter,
device_desc,
user_functions,
msaa_samples,
max_capture_frame_jobs,
capture_frame_timeout,
clear_color,
} = self;
if !title_was_set {
if let Ok(exe_path) = env::current_exe() {
if let Some(os_str) = exe_path.file_stem() {
if let Some(exe_name) = os_str.to_str() {
let title = format!("nannou - {}", exe_name);
window = window.with_title(title);
}
}
}
}
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
#[cfg(not(feature = "wayland"))]
{
use winit::platform::x11::WindowBuilderExtX11;
window = window.with_name("nannou".to_string(), "nannou".to_string());
}
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
#[cfg(feature = "wayland")]
{
use winit::platform::wayland::WindowBuilderExtWayland;
window = window.with_name("nannou".to_string(), "nannou".to_string());
}
let initial_window_size = window
.window_attributes()
.inner_size
.or_else(|| {
window
.window_attributes()
.fullscreen
.as_ref()
.and_then(|fullscreen| match fullscreen {
Fullscreen::Exclusive(video_mode) => {
let monitor = video_mode.monitor();
Some(
video_mode
.size()
.to_logical::<f32>(monitor.scale_factor())
.into(),
)
}
Fullscreen::Borderless(monitor) => monitor.as_ref().map(|monitor| {
monitor
.size()
.to_logical::<f32>(monitor.scale_factor())
.into()
}),
})
})
.unwrap_or_else(|| {
let mut dim = DEFAULT_DIMENSIONS;
if let Some(min) = window.window_attributes().min_inner_size {
match min {
winit::dpi::Size::Logical(min) => {
dim.width = dim.width.max(min.width as _);
dim.height = dim.height.max(min.height as _);
}
winit::dpi::Size::Physical(min) => {
dim.width = dim.width.max(min.width as _);
dim.height = dim.height.max(min.height as _);
unimplemented!("consider scale factor");
}
}
}
if let Some(max) = window.window_attributes().max_inner_size {
match max {
winit::dpi::Size::Logical(max) => {
dim.width = dim.width.min(max.width as _);
dim.height = dim.height.min(max.height as _);
}
winit::dpi::Size::Physical(max) => {
dim.width = dim.width.min(max.width as _);
dim.height = dim.height.min(max.height as _);
unimplemented!("consider scale factor");
}
}
}
dim.into()
});
if window.window_attributes().inner_size.is_none()
&& window.window_attributes().fullscreen.is_none()
{
window = window.with_inner_size(initial_window_size);
}
if window.window_attributes().min_inner_size.is_none()
&& window.window_attributes().fullscreen.is_none()
{
window = window.with_min_inner_size(winit::dpi::Size::Physical(MIN_SC_PIXELS));
}
let is_invalidated = true;
let clear_color = clear_color.unwrap_or_else(|| {
let mut color: wgpu::Color = Default::default();
color.a = if window.window_attributes().transparent {
0.0
} else {
1.0
};
color
});
let window = {
let window_target = app
.event_loop_window_target
.as_ref()
.expect("unexpected invalid App.event_loop_window_target state - please report")
.as_ref();
window.build(window_target)?
};
#[cfg(target_arch = "wasm32")]
{
use winit::platform::web::WindowExtWebSys;
let canvas = window.canvas();
web_sys::window()
.expect("window")
.document()
.expect("document")
.body()
.expect("body")
.append_child(&canvas)
.expect("append_child");
}
let surface = unsafe {
app.instance()
.create_surface(&window)
.expect("Could not create surface")
};
let request_adapter_opts = wgpu::RequestAdapterOptions {
power_preference,
compatible_surface: Some(&surface),
force_fallback_adapter,
};
let adapter = app
.wgpu_adapters()
.get_or_request_async(request_adapter_opts, app.instance())
.await
.ok_or(BuildError::NoAvailableAdapter)?;
let device_desc = device_desc.unwrap_or_else(wgpu::default_device_descriptor);
let device_queue_pair = adapter.get_or_request_device_async(device_desc).await;
let win_physical_size = window.inner_size();
let win_dims_px: [u32; 2] = win_physical_size.into();
let device = device_queue_pair.device();
let surface_conf = surface_conf_builder.build(&surface, &*adapter, win_dims_px);
surface.configure(&device, &surface_conf);
let (frame_data, msaa_samples) = match user_functions.view {
Some(View::WithModel(_)) | Some(View::Sketch(_)) | None => {
let msaa_samples = msaa_samples.unwrap_or(Frame::DEFAULT_MSAA_SAMPLES);
let surface_dims = [surface_conf.width, surface_conf.height];
let render = frame::RenderData::new(
&device,
surface_dims,
surface_conf.format,
msaa_samples,
);
let capture =
frame::CaptureData::new(max_capture_frame_jobs, capture_frame_timeout);
let frame_data = FrameData { render, capture };
(Some(frame_data), msaa_samples)
}
Some(View::WithModelRaw(_)) => (None, 1),
};
let window_id = window.id();
let frame_count = 0;
let tracked_state = TrackedState {
scale_factor: window.scale_factor(),
physical_size: win_physical_size,
};
let window = Window {
window,
surface,
surface_conf,
device_queue_pair,
msaa_samples,
frame_data,
frame_count,
user_functions,
tracked_state,
is_invalidated,
clear_color,
};
app.windows.borrow_mut().insert(window_id, window);
if app.windows.borrow().len() == 1 {
*app.focused_window.borrow_mut() = Some(window_id);
}
Ok(window_id)
}
fn map_window<F>(self, map: F) -> Self
where
F: FnOnce(winit::window::WindowBuilder) -> winit::window::WindowBuilder,
{
let Builder {
app,
window,
title_was_set,
device_desc,
power_preference,
force_fallback_adapter,
surface_conf_builder,
user_functions,
msaa_samples,
max_capture_frame_jobs,
capture_frame_timeout,
clear_color,
} = self;
let window = map(window);
Builder {
app,
window,
title_was_set,
device_desc,
power_preference,
force_fallback_adapter,
surface_conf_builder,
user_functions,
msaa_samples,
max_capture_frame_jobs,
capture_frame_timeout,
clear_color,
}
}
pub fn size(self, width: u32, height: u32) -> Self {
self.map_window(|w| w.with_inner_size(winit::dpi::LogicalSize { width, height }))
}
pub fn min_size(self, width: u32, height: u32) -> Self {
self.map_window(|w| w.with_min_inner_size(winit::dpi::LogicalSize { width, height }))
}
pub fn max_size(self, width: u32, height: u32) -> Self {
self.map_window(|w| w.with_max_inner_size(winit::dpi::LogicalSize { width, height }))
}
pub fn size_pixels(self, width: u32, height: u32) -> Self {
self.map_window(|w| w.with_inner_size(winit::dpi::PhysicalSize { width, height }))
}
pub fn resizable(self, resizable: bool) -> Self {
self.map_window(|w| w.with_resizable(resizable))
}
pub fn title<T>(mut self, title: T) -> Self
where
T: Into<String>,
{
self.title_was_set = true;
self.map_window(|w| w.with_title(title))
}
pub fn fullscreen(self) -> Self {
let fullscreen = Fullscreen::Borderless(self.app.primary_monitor());
self.fullscreen_with(Some(fullscreen))
}
pub fn fullscreen_with(self, fullscreen: Option<Fullscreen>) -> Self {
self.map_window(|w| w.with_fullscreen(fullscreen))
}
pub fn maximized(self, maximized: bool) -> Self {
self.map_window(|w| w.with_maximized(maximized))
}
pub fn visible(self, visible: bool) -> Self {
self.map_window(|w| w.with_visible(visible))
}
pub fn transparent(self, transparent: bool) -> Self {
self.map_window(|w| w.with_transparent(transparent))
}
pub fn decorations(self, decorations: bool) -> Self {
self.map_window(|w| w.with_decorations(decorations))
}
pub fn always_on_top(self, always_on_top: bool) -> Self {
self.map_window(|w| {
w.with_window_level(if always_on_top {
WindowLevel::AlwaysOnBottom
} else {
WindowLevel::Normal
})
})
}
pub fn window_icon(self, window_icon: Option<winit::window::Icon>) -> Self {
self.map_window(|w| w.with_window_icon(window_icon))
}
}
impl Window {
pub fn id(&self) -> Id {
self.window.id()
}
pub fn scale_factor(&self) -> geom::scalar::Default {
self.window.scale_factor() as _
}
pub fn outer_position_pixels(&self) -> Result<(i32, i32), winit::error::NotSupportedError> {
self.window.outer_position().map(Into::into)
}
pub fn set_outer_position_pixels(&self, x: i32, y: i32) {
self.window
.set_outer_position(winit::dpi::PhysicalPosition { x, y })
}
pub fn inner_size_pixels(&self) -> (u32, u32) {
self.window.inner_size().into()
}
pub fn inner_size_points(&self) -> (geom::scalar::Default, geom::scalar::Default) {
self.window
.inner_size()
.to_logical::<f32>(self.tracked_state.scale_factor)
.into()
}
pub fn set_inner_size_pixels(&self, width: u32, height: u32) {
self.window
.set_inner_size(winit::dpi::PhysicalSize { width, height })
}
pub fn set_inner_size_points(&self, width: f32, height: f32) {
self.window
.set_inner_size(winit::dpi::LogicalSize { width, height })
}
pub fn outer_size_pixels(&self) -> (u32, u32) {
self.window.outer_size().into()
}
pub fn outer_size_points(&self) -> (f32, f32) {
self.window
.outer_size()
.to_logical::<f32>(self.tracked_state.scale_factor)
.into()
}
pub fn set_min_inner_size_points(&self, size: Option<(f32, f32)>) {
let size = size.map(|(width, height)| winit::dpi::LogicalSize { width, height });
self.window.set_min_inner_size(size)
}
pub fn set_max_inner_size_points(&self, size: Option<(f32, f32)>) {
let size = size.map(|(width, height)| winit::dpi::LogicalSize { width, height });
self.window.set_max_inner_size(size)
}
pub fn set_title(&self, title: &str) {
self.window.set_title(title);
}
pub fn set_visible(&self, visible: bool) {
self.window.set_visible(visible)
}
pub fn set_resizable(&self, resizable: bool) {
self.window.set_resizable(resizable)
}
pub fn set_minimized(&self, minimized: bool) {
self.window.set_minimized(minimized)
}
pub fn set_maximized(&self, maximized: bool) {
self.window.set_maximized(maximized)
}
pub fn set_fullscreen(&self, fullscreen: bool) {
if fullscreen {
let monitor = self.current_monitor();
let fullscreen = Fullscreen::Borderless(monitor);
self.set_fullscreen_with(Some(fullscreen));
} else {
self.set_fullscreen_with(None);
}
}
pub fn set_fullscreen_with(&self, fullscreen: Option<Fullscreen>) {
self.window.set_fullscreen(fullscreen)
}
pub fn fullscreen(&self) -> Option<Fullscreen> {
self.window.fullscreen()
}
pub fn set_decorations(&self, decorations: bool) {
self.window.set_decorations(decorations)
}
pub fn set_always_on_top(&self, always_on_top: bool) {
self.window.set_window_level(if always_on_top {
WindowLevel::AlwaysOnTop
} else {
WindowLevel::Normal
})
}
pub fn set_window_icon(&self, window_icon: Option<winit::window::Icon>) {
self.window.set_window_icon(window_icon)
}
pub fn set_ime_position_points(&self, x: f32, y: f32) {
self.window
.set_ime_position(winit::dpi::LogicalPosition { x, y })
}
pub fn set_cursor_icon(&self, state: winit::window::CursorIcon) {
self.window.set_cursor_icon(state);
}
pub fn set_cursor_position_points(
&self,
x: f32,
y: f32,
) -> Result<(), winit::error::ExternalError> {
self.window
.set_cursor_position(winit::dpi::LogicalPosition { x, y })
}
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), winit::error::ExternalError> {
self.window.set_cursor_grab(if grab {
CursorGrabMode::Locked
} else {
CursorGrabMode::None
})
}
pub fn set_cursor_visible(&self, visible: bool) {
self.window.set_cursor_visible(visible)
}
pub fn current_monitor(&self) -> Option<winit::monitor::MonitorHandle> {
self.window.current_monitor()
}
pub fn surface(&self) -> &wgpu::Surface {
&self.surface
}
pub fn surface_configuration(&self) -> &wgpu::SurfaceConfiguration {
&self.surface_conf
}
pub fn device(&self) -> &wgpu::Device {
self.device_queue_pair.device()
}
pub fn queue(&self) -> &wgpu::Queue {
self.device_queue_pair.queue()
}
pub fn device_queue_pair(&self) -> &Arc<wgpu::DeviceQueuePair> {
&self.device_queue_pair
}
pub fn msaa_samples(&self) -> u32 {
self.msaa_samples
}
pub(crate) fn reconfigure_surface(&mut self, [w_px, h_px]: [u32; 2]) {
self.tracked_state.physical_size.width = w_px.max(MIN_SC_PIXELS.width);
self.tracked_state.physical_size.height = h_px.max(MIN_SC_PIXELS.height);
self.surface_conf.width = self.tracked_state.physical_size.width;
self.surface_conf.height = self.tracked_state.physical_size.height;
self.surface.configure(self.device(), &self.surface_conf);
if self.frame_data.is_some() {
let render_data = frame::RenderData::new(
self.device(),
self.tracked_state.physical_size.into(),
self.surface_conf.format,
self.msaa_samples,
);
self.frame_data.as_mut().unwrap().render = render_data;
}
self.is_invalidated = true;
}
pub fn is_fullscreen(&self) -> bool {
self.fullscreen().is_some()
}
pub fn elapsed_frames(&self) -> u64 {
self.frame_count
}
pub fn rect(&self) -> geom::Rect {
let (w, h) = self.inner_size_points();
geom::Rect::from_w_h(w, h)
}
pub fn capture_frame<P>(&self, path: P)
where
P: AsRef<Path>,
{
self.capture_frame_inner(path.as_ref());
}
pub fn winit_window(&self) -> &winit::window::Window {
&self.window
}
fn capture_frame_inner(&self, path: &Path) {
let dir = path.parent().expect("capture_frame path has no directory");
if !dir.exists() {
std::fs::create_dir_all(&dir).expect("failed to create `capture_frame` directory");
}
let mut capture_next_frame_path = self
.frame_data
.as_ref()
.expect("window capture requires that `view` draws to a `Frame` (not a `RawFrame`)")
.capture
.next_frame_path
.lock()
.expect("failed to lock `capture_next_frame_path`");
*capture_next_frame_path = Some(path.to_path_buf());
}
pub fn await_capture_frame_jobs(
&self,
) -> Result<(), wgpu::TextureCapturerAwaitWorkerTimeout<()>> {
if let Some(frame_data) = self.frame_data.as_ref() {
let capture_data = &frame_data.capture;
let device = self.device();
return capture_data.texture_capturer.await_active_snapshots(device);
}
Ok(())
}
}
impl Drop for Window {
fn drop(&mut self) {
if self.await_capture_frame_jobs().is_err() {
eprintln!("timed out while waiting for capture jobs to complete");
}
}
}
impl fmt::Debug for View {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let variant = match *self {
View::WithModel(ref v) => format!("WithModel({:?})", v),
View::WithModelRaw(ref v) => format!("WithModelRaw({:?})", v),
View::Sketch(_) => "Sketch".to_string(),
};
write!(f, "View::{}", variant)
}
}
impl fmt::Display for BuildError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
BuildError::NoAvailableAdapter => write!(f, "no available wgpu adapter detected"),
BuildError::WinitOsError(ref e) => e.fmt(f),
}
}
}
impl From<winit::error::OsError> for BuildError {
fn from(e: winit::error::OsError) -> Self {
BuildError::WinitOsError(e)
}
}
impl<'a> wgpu::WithDeviceQueuePair for &'a crate::window::Window {
fn with_device_queue_pair<F, O>(self, f: F) -> O
where
F: FnOnce(&wgpu::Device, &wgpu::Queue) -> O,
{
self.device_queue_pair().with_device_queue_pair(f)
}
}