use crate::{context::VulkanoContext, renderer::VulkanoWindowRenderer};
use foldhash::HashMap;
use std::collections::hash_map::{Iter, IterMut};
use vulkano::swapchain::{PresentMode, SwapchainCreateInfo};
use winit::{
dpi::LogicalSize,
event_loop::ActiveEventLoop,
window::{CursorGrabMode, WindowId},
};
#[derive(Default)]
pub struct VulkanoWindows {
windows: HashMap<WindowId, VulkanoWindowRenderer>,
primary: Option<WindowId>,
}
impl VulkanoWindows {
pub fn create_window(
&mut self,
event_loop: &ActiveEventLoop,
vulkano_context: &VulkanoContext,
window_descriptor: &WindowDescriptor,
swapchain_create_info_modify: fn(&mut SwapchainCreateInfo),
) -> WindowId {
let mut winit_window_attributes = winit::window::Window::default_attributes();
winit_window_attributes = match window_descriptor.mode {
WindowMode::BorderlessFullscreen => winit_window_attributes.with_fullscreen(Some(
winit::window::Fullscreen::Borderless(event_loop.primary_monitor()),
)),
WindowMode::Fullscreen => {
winit_window_attributes.with_fullscreen(Some(winit::window::Fullscreen::Exclusive(
get_best_videomode(&event_loop.primary_monitor().unwrap()),
)))
}
WindowMode::SizedFullscreen => winit_window_attributes.with_fullscreen(Some(
winit::window::Fullscreen::Exclusive(get_fitting_videomode(
&event_loop.primary_monitor().unwrap(),
window_descriptor.width as u32,
window_descriptor.height as u32,
)),
)),
_ => {
let WindowDescriptor {
width,
height,
position,
scale_factor_override,
..
} = window_descriptor;
if let Some(position) = position {
if let Some(sf) = scale_factor_override {
winit_window_attributes = winit_window_attributes.with_position(
winit::dpi::LogicalPosition::new(
position[0] as f64,
position[1] as f64,
)
.to_physical::<f64>(*sf),
);
} else {
winit_window_attributes = winit_window_attributes.with_position(
winit::dpi::LogicalPosition::new(
position[0] as f64,
position[1] as f64,
),
);
}
}
if let Some(sf) = scale_factor_override {
winit_window_attributes
.with_inner_size(LogicalSize::new(*width, *height).to_physical::<f64>(*sf))
} else {
winit_window_attributes.with_inner_size(LogicalSize::new(*width, *height))
}
}
.with_resizable(window_descriptor.resizable)
.with_decorations(window_descriptor.decorations)
.with_transparent(window_descriptor.transparent),
};
let constraints = window_descriptor.resize_constraints.check_constraints();
let min_inner_size = LogicalSize {
width: constraints.min_width,
height: constraints.min_height,
};
let max_inner_size = LogicalSize {
width: constraints.max_width,
height: constraints.max_height,
};
let winit_window_attributes =
if constraints.max_width.is_finite() && constraints.max_height.is_finite() {
winit_window_attributes
.with_min_inner_size(min_inner_size)
.with_max_inner_size(max_inner_size)
} else {
winit_window_attributes.with_min_inner_size(min_inner_size)
};
#[allow(unused_mut)]
let mut winit_window_attributes =
winit_window_attributes.with_title(&window_descriptor.title);
let winit_window = event_loop.create_window(winit_window_attributes).unwrap();
if window_descriptor.cursor_locked {
match winit_window.set_cursor_grab(CursorGrabMode::Confined) {
Ok(_) => {}
Err(winit::error::ExternalError::NotSupported(_)) => {}
Err(err) => panic!("{:?}", err),
}
}
winit_window.set_cursor_visible(window_descriptor.cursor_visible);
let id = winit_window.id();
if self.primary.is_none() {
self.primary = Some(id);
}
self.windows.insert(
id,
VulkanoWindowRenderer::new(
vulkano_context,
winit_window,
window_descriptor,
swapchain_create_info_modify,
),
);
id
}
#[inline]
pub fn get_primary_renderer_mut(&mut self) -> Option<&mut VulkanoWindowRenderer> {
if self.primary.is_some() {
self.get_renderer_mut(self.primary.unwrap())
} else {
None
}
}
#[inline]
pub fn get_primary_renderer(&self) -> Option<&VulkanoWindowRenderer> {
if self.primary.is_some() {
self.get_renderer(self.primary.unwrap())
} else {
None
}
}
#[inline]
pub fn get_primary_window(&self) -> Option<&winit::window::Window> {
if self.primary.is_some() {
self.get_window(self.primary.unwrap())
} else {
None
}
}
#[inline]
pub fn get_renderer_mut(&mut self, id: WindowId) -> Option<&mut VulkanoWindowRenderer> {
self.windows.get_mut(&id)
}
#[inline]
pub fn get_renderer(&self, id: WindowId) -> Option<&VulkanoWindowRenderer> {
self.windows.get(&id)
}
#[inline]
pub fn get_window(&self, id: WindowId) -> Option<&winit::window::Window> {
self.windows.get(&id).map(|v_window| v_window.window())
}
#[inline]
pub fn primary_window_id(&self) -> Option<WindowId> {
self.primary
}
#[inline]
pub fn remove_renderer(&mut self, id: WindowId) {
self.windows.remove(&id);
if let Some(primary) = self.primary {
if primary == id {
self.primary = None;
}
}
}
#[inline]
pub fn iter(&self) -> Iter<'_, WindowId, VulkanoWindowRenderer> {
self.windows.iter()
}
#[inline]
pub fn iter_mut(&mut self) -> IterMut<'_, WindowId, VulkanoWindowRenderer> {
self.windows.iter_mut()
}
}
fn get_fitting_videomode(
monitor: &winit::monitor::MonitorHandle,
width: u32,
height: u32,
) -> winit::monitor::VideoModeHandle {
let mut modes = monitor.video_modes().collect::<Vec<_>>();
fn abs_diff(a: u32, b: u32) -> u32 {
if a > b {
return a - b;
}
b - a
}
modes.sort_by(|a, b| {
use std::cmp::Ordering::*;
match abs_diff(a.size().width, width).cmp(&abs_diff(b.size().width, width)) {
Equal => {
match abs_diff(a.size().height, height).cmp(&abs_diff(b.size().height, height)) {
Equal => b
.refresh_rate_millihertz()
.cmp(&a.refresh_rate_millihertz()),
default => default,
}
}
default => default,
}
});
modes.first().unwrap().clone()
}
fn get_best_videomode(monitor: &winit::monitor::MonitorHandle) -> winit::monitor::VideoModeHandle {
let mut modes = monitor.video_modes().collect::<Vec<_>>();
modes.sort_by(|a, b| {
use std::cmp::Ordering::*;
match b.size().width.cmp(&a.size().width) {
Equal => match b.size().height.cmp(&a.size().height) {
Equal => b
.refresh_rate_millihertz()
.cmp(&a.refresh_rate_millihertz()),
default => default,
},
default => default,
}
});
modes.first().unwrap().clone()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WindowMode {
Windowed,
BorderlessFullscreen,
SizedFullscreen,
Fullscreen,
}
#[derive(Debug, Clone)]
pub struct WindowDescriptor {
pub width: f32,
pub height: f32,
pub position: Option<[f32; 2]>,
pub resize_constraints: WindowResizeConstraints,
pub scale_factor_override: Option<f64>,
pub title: String,
pub present_mode: PresentMode,
pub resizable: bool,
pub decorations: bool,
pub cursor_visible: bool,
pub cursor_locked: bool,
pub mode: WindowMode,
pub transparent: bool,
}
impl Default for WindowDescriptor {
#[inline]
fn default() -> Self {
WindowDescriptor {
title: "Vulkano App".to_string(),
width: 1280.,
height: 720.,
position: None,
resize_constraints: WindowResizeConstraints::default(),
scale_factor_override: None,
present_mode: PresentMode::Fifo,
resizable: true,
decorations: true,
cursor_locked: false,
cursor_visible: true,
mode: WindowMode::Windowed,
transparent: false,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct WindowResizeConstraints {
pub min_width: f32,
pub min_height: f32,
pub max_width: f32,
pub max_height: f32,
}
impl Default for WindowResizeConstraints {
#[inline]
fn default() -> Self {
Self {
min_width: 180.,
min_height: 120.,
max_width: f32::INFINITY,
max_height: f32::INFINITY,
}
}
}
impl WindowResizeConstraints {
#[must_use]
pub fn check_constraints(&self) -> Self {
let &WindowResizeConstraints {
mut min_width,
mut min_height,
mut max_width,
mut max_height,
} = self;
min_width = min_width.max(1.);
min_height = min_height.max(1.);
if max_width < min_width {
println!(
"The given maximum width {} is smaller than the minimum width {}",
max_width, min_width
);
max_width = min_width;
}
if max_height < min_height {
println!(
"The given maximum height {} is smaller than the minimum height {}",
max_height, min_height
);
max_height = min_height;
}
WindowResizeConstraints {
min_width,
min_height,
max_width,
max_height,
}
}
}