diff options
Diffstat (limited to 'src/window.rs')
-rw-r--r-- | src/window.rs | 154 |
1 files changed, 111 insertions, 43 deletions
diff --git a/src/window.rs b/src/window.rs index ffe35e8..697c0bb 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,6 +1,6 @@ use crate::*; -use std::num::NonZeroU32; +use buffer::HasDimensions; use winit::dpi::{Size, PhysicalSize}; use winit::event_loop::EventLoopWindowTarget; use winit::window::WindowId; @@ -8,35 +8,50 @@ use winit::window::WindowId; pub struct Window { pub controller: Box<dyn WindowController>, window: winit::window::Window, + pixel_scale: u32, buffer: Buffer, - dimensions: Dimensions, - #[allow(dead_code)] context: softbuffer::Context, surface: softbuffer::Surface, + surface_dimensions: Dimensions, + #[allow(dead_code)] context: softbuffer::Context, current_render_hint: RenderHint, previous_cursor_position: Option<Point>, } impl Window { pub fn new(event_loop: &EventLoopWindowTarget<()>, controller: Box<dyn WindowController>) -> Self { - let window = winit::window::WindowBuilder::new() - .with_title(controller.title()) - .with_resizable(controller.is_resizable()) - .with_inner_size(dim_to_size(controller.initial_size())) - .build(event_loop) - .unwrap(); + let mut builder = winit::window::WindowBuilder::new().with_title(controller.title()); + let pixel_scale = controller.pixel_scale().get(); + if let Some(exact_dimensions) = controller.exact_size() { + let exact_size = dim_to_size(exact_dimensions * pixel_scale); + builder = builder + .with_resizable(false) + .with_inner_size(exact_size) + .with_min_inner_size(exact_size) + .with_max_inner_size(exact_size); + } else if let Some(initial_dimensions) = controller.initial_size() { + builder = builder.with_inner_size(dim_to_size(initial_dimensions * pixel_scale)); + if let Some(min_dimensions) = controller.minimum_size() { + builder = builder.with_min_inner_size(dim_to_size(min_dimensions * pixel_scale)); + } + if let Some(max_dimensions) = controller.maximum_size() { + builder = builder.with_max_inner_size(dim_to_size(max_dimensions * pixel_scale)); + } + }; + let window = builder.build(event_loop).unwrap(); let context = unsafe { softbuffer::Context::new(&window) }.unwrap(); let surface = unsafe { softbuffer::Surface::new(&context, &window) }.unwrap(); Self { controller, window, + pixel_scale, buffer: Buffer::new(Dimensions::ZERO), - dimensions: Dimensions::ZERO, - context, surface, - previous_cursor_position: None, + surface_dimensions: Dimensions::ZERO, + context, current_render_hint: RenderHint::Redraw, + previous_cursor_position: None, } } @@ -53,40 +68,52 @@ impl Window { self.window.set_cursor_icon(icon); } - pub fn update_minimum_size(&mut self) { - let size = self.controller.minimum_size().map(|d| dim_to_size(d)); - self.window.set_min_inner_size(size); - } - - pub fn update_maximum_size(&mut self) { - let size = self.controller.maximum_size().map(|d| dim_to_size(d)); - self.window.set_max_inner_size(size); + /// This method only requests that the window size be changed; it does not + /// alter any buffer sizes or struct fields. When the window size eventually + /// changes, the state updates are handled by `resize_buffer_and_surface`. + pub fn update_window_size(&mut self) { + let pixel_scale = self.controller.pixel_scale().get(); + if let Some(dimensions) = self.controller.exact_size() { + // Without this early return, the constant re-setting of the window + // size prevents the window from being able to be positioned with the + // bottom edge below the screen bounds. + if pixel_scale == self.pixel_scale + && dimensions * pixel_scale == self.surface_dimensions { + return; + } + self.window.set_resizable(false); + let size = dim_to_size(dimensions * pixel_scale); + self.window.set_min_inner_size(Some(size)); + self.window.set_max_inner_size(Some(size)); + self.window.set_inner_size(size); + } else { + self.window.set_resizable(true); + let size = self.controller.minimum_size().map(|d| dim_to_size(d * pixel_scale)); + self.window.set_min_inner_size(size); + let size = self.controller.maximum_size().map(|d| dim_to_size(d * pixel_scale)); + self.window.set_max_inner_size(size); + } } pub fn update_cursor_visible(&mut self) { self.window.set_cursor_visible(self.controller.is_cursor_visible()); } - pub fn update_resizable(&mut self) { - let is_resizable = self.controller.is_resizable(); - self.window.set_resizable(is_resizable); - // Hack to force window to be impossible to resize on DWM, where the - // 'is_resizable' window attribute isn't respected. - if !is_resizable { - self.window.set_min_inner_size(Some(self.window.inner_size())); - self.window.set_max_inner_size(Some(self.window.inner_size())); + /// Resize the frame buffer and screen surface to the new size of the window. + pub fn resize_buffer_and_surface(&mut self, physical_dimensions: Dimensions) { + if self.surface_dimensions == physical_dimensions { + return; } - } - - /// Resize the frame buffer to be the new size of the window. - pub fn resize(&mut self, dimensions: Dimensions) { - if self.dimensions == dimensions { return } - self.dimensions = dimensions; - self.buffer.resize(dimensions); - if let Some((width, height)) = dim_to_nonzero_size(dimensions) { + if let Some((width, height)) = dim_to_nonzero_size(physical_dimensions) { self.surface.resize(width, height).unwrap(); + self.surface_dimensions = physical_dimensions; }; - self.controller.on_resize(dimensions); + let pixel_scale = self.controller.pixel_scale().get(); + let logical_dimensions = physical_dimensions / pixel_scale; + self.buffer.resize(logical_dimensions); + self.controller.on_resize(logical_dimensions); + + self.pixel_scale = pixel_scale; self.current_render_hint = RenderHint::Redraw; } @@ -107,16 +134,57 @@ impl Window { } pub fn render(&mut self) { - let size = self.window.inner_size(); - let dim = Dimensions::new(size.width, size.height); - self.resize(dim); + // If the surface dimensions are zero, the first resize event hasn't yet + // come through, and the Surface object will panic when `.present()` is + // called on it later in this method. We will instead skip rendering for + // this frame, knowing that the pending resize will trigger a re-render. + if self.surface_dimensions.is_zero() { + return; + } + + let pixel_scale = self.pixel_scale; + let physical_dim = self.surface_dimensions; + let logical_dim = self.buffer.dimensions(); self.controller.on_render(&mut self.buffer, self.current_render_hint); let buffer = self.buffer.as_u32_slice(); let mut surface = self.surface.buffer_mut().unwrap(); - let buffer_len = buffer.len(); - let surface_len = surface.len(); - surface[..buffer_len].copy_from_slice(&buffer[..surface_len]); + + if self.pixel_scale == 1 { + let overlap = std::cmp::min(buffer.len(), surface.len()); + surface[..overlap].copy_from_slice(&buffer[..overlap]); + } else { + let logical_content_width = logical_dim.width as usize; + let physical_content_width = (logical_dim.width * pixel_scale) as usize; + let row_excess = (physical_dim.width as usize).saturating_sub(physical_content_width); + let mut bi: usize = 0; + let mut si: usize = 0; + for _y in 0..logical_dim.height { + for _py in 0..pixel_scale { + for _x in 0..logical_dim.width { + for _px in 0..pixel_scale { + surface[si] = buffer[bi]; + si += 1; + } + bi += 1; + } + // Fill the excess space on the right edge with black. + for _ in 0..row_excess { + surface[si] = 0; + si += 1; + } + bi -= logical_content_width; + } + bi += logical_content_width; + } + // Fill the excess space on the bottom edge with black. + let excess = surface.len().saturating_sub(si); + for _ in 0..excess { + surface[si] = 0; + si += 1; + } + } surface.present().unwrap(); + // Reset current_render_hint back to the lowest variant for the next frame. self.current_render_hint = RenderHint::Update; } |