diff options
| author | HampusM <hampus@hampusmat.com> | 2026-06-28 00:09:06 +0200 |
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2026-06-30 18:24:47 +0200 |
| commit | 9df8a4d197e66accb389edb5a5c54117933f157e (patch) | |
| tree | 2d348fea32df9dd73ec6b961fae63e8ccb60899d /engine | |
| parent | 4a5e3e612e8b597dd1734afefd01324b74fd99ab (diff) | |
feat(engine): add windowing DPI utilities
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/src/rendering.rs | 3 | ||||
| -rw-r--r-- | engine/src/rendering/backend/opengl.rs | 21 | ||||
| -rw-r--r-- | engine/src/shader/default.rs | 3 | ||||
| -rw-r--r-- | engine/src/ui/imgui.rs | 15 | ||||
| -rw-r--r-- | engine/src/windowing.rs | 28 | ||||
| -rw-r--r-- | engine/src/windowing/dpi.rs | 182 | ||||
| -rw-r--r-- | engine/src/windowing/mouse.rs | 3 | ||||
| -rw-r--r-- | engine/src/windowing/window.rs | 41 |
8 files changed, 234 insertions, 62 deletions
diff --git a/engine/src/rendering.rs b/engine/src/rendering.rs index e9e620e..47d6e98 100644 --- a/engine/src/rendering.rs +++ b/engine/src/rendering.rs @@ -30,6 +30,7 @@ use crate::shader::cursor::{ use crate::shader::Program as ShaderProgram; use crate::texture::Texture; use crate::vector::Vec2; +use crate::windowing::dpi::PhysicalSize; use crate::windowing::window::Window; pub mod backend; @@ -228,7 +229,7 @@ pub enum Command { RemoveSurface(SurfaceId), MakeCurrent(SurfaceId), - SetSurfaceSize(SurfaceId, Dimens<u32>), + SetSurfaceSize(SurfaceId, PhysicalSize<u32>), ClearBuffers(BufferClearMask), SwapBuffers(SurfaceId), CreateShaderProgram(ObjectId, ShaderProgram), diff --git a/engine/src/rendering/backend/opengl.rs b/engine/src/rendering/backend/opengl.rs index c1e4823..f8be5b4 100644 --- a/engine/src/rendering/backend/opengl.rs +++ b/engine/src/rendering/backend/opengl.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::collections::HashMap; +use std::num::NonZero; use ecs::query::term::With; use glutin::config::Config as GlutinConfig; @@ -119,6 +120,7 @@ use crate::texture::{ }; use crate::util::OptionExt; use crate::vector::{Vec2, Vec3}; +use crate::windowing::dpi::PhysicalSize; use crate::windowing::window::{ Closed as WindowClosed, CreationAttributes as WindowCreationAttributes, @@ -151,7 +153,7 @@ struct GraphicsContext struct GraphicsContextSurface { window_surface: GlutinSurface<GlutinWindowSurface>, - size: Dimens<u32>, + size: PhysicalSize<u32>, } #[derive(Debug)] @@ -299,7 +301,7 @@ fn init_window_graphics( } }; - let Some(window_inner_size) = window.inner_size.clone().try_into_nonzero() else { + let Ok(window_inner_size) = PhysicalSize::<NonZero<u32>>::try_convert_from(window.inner_size.clone()) else { tracing::error!( "Cannot create a surface for a window with a width/height of 0", ); @@ -349,7 +351,10 @@ fn init_window_graphics( if let Err(err) = gl_set_viewport( &gl_context, &Vec2 { x: 0, y: 0 }.into(), - &window.inner_size.clone().into(), + &opengl_bindings::data_types::Dimens { + width: window.inner_size.width, + height: window.inner_size.height + } ) { tracing::error!("Failed to set viewport: {err}"); } @@ -467,7 +472,10 @@ fn handle_commands( if let Err(err) = gl_set_viewport( gl_context, &Vec2 { x: 0, y: 0 }.into(), - &surface.size.into(), + &opengl_bindings::data_types::Dimens { + width: surface.size.width, + height: surface.size.height + } ) { tracing::error!("Failed to set viewport: {err}"); } @@ -487,7 +495,10 @@ fn handle_commands( if let Err(err) = gl_set_viewport( gl_context, &Vec2 { x: 0, y: 0 }.into(), - &surface.size.into(), + &opengl_bindings::data_types::Dimens { + width: surface.size.width, + height: surface.size.height + } ) { tracing::error!("Failed to set viewport: {err}"); } diff --git a/engine/src/shader/default.rs b/engine/src/shader/default.rs index cac4c4b..eafe024 100644 --- a/engine/src/shader/default.rs +++ b/engine/src/shader/default.rs @@ -26,6 +26,7 @@ use crate::shader::{ use crate::texture::WHITE_1X1_ASSET_LABEL as TEXTURE_WHITE_1X1_ASSET_LABEL; use crate::transform::{Scale, Transform, WorldPosition}; use crate::vector::Vec3; +use crate::windowing::dpi::PhysicalSize; use crate::windowing::window::Window; pub static ASSET_LABEL: LazyLock<AssetLabel> = LazyLock::new(|| AssetLabel { @@ -340,7 +341,7 @@ fn create_view_matrix(camera: &Camera, camera_world_pos: Vec3<f32>) -> Matrix<f3 fn create_projection_matrix( camera: &Camera, - window_size: &Dimens<u32>, + window_size: &PhysicalSize<u32>, ) -> Matrix<f32, 4, 4> { match &camera.projection { diff --git a/engine/src/ui/imgui.rs b/engine/src/ui/imgui.rs index aa90e9f..daf8494 100644 --- a/engine/src/ui/imgui.rs +++ b/engine/src/ui/imgui.rs @@ -73,6 +73,7 @@ use crate::texture::{ Wrapping as TextureWrapping, }; use crate::vector::Vec2; +use crate::windowing::dpi::PhysicalSize; use crate::windowing::window::Window; mod reexports @@ -182,17 +183,15 @@ fn handle_window_changed( .get_io_mut() .set_display_framebuffer_scale([hidpi_factor as f32, hidpi_factor as f32]); - let window_size = Dimens { - width: window.inner_size.width as f32, - height: window.inner_size.height as f32, - }; - - let window_logical_size = window_size / (hidpi_factor as f32); + let window_size_logical = PhysicalSize { + width: window.inner_size.width as f64, + height: window.inner_size.height as f64, + }.to_logical::<f64>(hidpi_factor); context .ctx .get_io_mut() - .set_display_size([window_logical_size.width, window_logical_size.height]); + .set_display_size([window_size_logical.width as f32, window_size_logical.height as f32]); } fn update( @@ -494,7 +493,7 @@ fn update_inputs( context.ctx.get_io_mut().add_input_character(character); } - let mouse_pos = mouse.position / window.scale_factor(); + let mouse_pos = mouse.position.to_logical::<f64>(window.scale_factor()); context .ctx diff --git a/engine/src/windowing.rs b/engine/src/windowing.rs index 5866c47..c79812a 100644 --- a/engine/src/windowing.rs +++ b/engine/src/windowing.rs @@ -11,7 +11,6 @@ use ecs::error::Error as EcsError; use raw_window_handle::{DisplayHandle, HandleError, HasDisplayHandle, WindowHandle}; use rfd::{MessageButtons, MessageDialog, MessageLevel}; use winit::application::ApplicationHandler; -use winit::dpi::PhysicalPosition; use winit::error::EventLoopError; use winit::event::{DeviceEvent, DeviceId, StartCause, WindowEvent}; use winit::event_loop::{ @@ -23,7 +22,6 @@ use winit::event_loop::{ use winit::keyboard::PhysicalKey; use winit::window::{Window as WinitWindow, WindowId as WinitWindowId}; -use crate::data_types::dimens::Dimens; use crate::ecs::actions::Actions; use crate::ecs::component::Component; use crate::ecs::entity::obtainer::Obtainer as EntityObtainer; @@ -35,6 +33,7 @@ use crate::ecs::system::observer::Observe; use crate::ecs::uid::Uid; use crate::ecs::{declare_entity, Query, Sole}; use crate::util::{AtomicTwoF64, MapVec}; +use crate::windowing::dpi::{PhysicalPosition, PhysicalSize, Position}; use crate::windowing::keyboard::{Key, KeyState, Keyboard, UnknownKeyCodeError}; use crate::windowing::mouse::{ Button as MouseButton, @@ -51,6 +50,7 @@ use crate::windowing::window::{ Window, }; +pub mod dpi; pub mod keyboard; pub mod mouse; pub mod window; @@ -178,11 +178,15 @@ fn update_stuff( .swap((0.0, 0.0), Ordering::Relaxed) .into(); - mouse.position = context + let absolute_mouse_pos = context .shared_state .absolute_mouse_pos - .load(Ordering::Relaxed) - .into(); + .load(Ordering::Relaxed); + + mouse.position = PhysicalPosition { + x: absolute_mouse_pos.0, + y: absolute_mouse_pos.1 + }; keyboard.set_text_keys(iter_array_queue(&context.shared_state.text_keys)); @@ -248,7 +252,7 @@ fn update_stuff( continue; }; - window.set_inner_size(new_window_size); + window.inner_size = new_window_size; window.set_changed(); } @@ -338,7 +342,7 @@ fn handle_window_changed( let window_apply_results = window.apply(winit_window); if let Err(actual_window_inner_size) = window_apply_results.inner_size_request { - window.set_inner_size(actual_window_inner_size); + window.inner_size = actual_window_inner_size; } context.try_send_message_to_app(MessageToApp::SetWindowCursorGrabMode( @@ -547,7 +551,7 @@ enum MessageFromApp { Init(OwnedDisplayHandle), WindowCreated(Uid, Arc<WinitWindow>, WindowCreationAttributes), - WindowResized(WindowId, Dimens<u32>), + WindowResized(WindowId, PhysicalSize<u32>), WindowCloseRequested(WindowId), WindowScaleFactorChanged(WindowId, f64), KeyboardKeyStateChanged(Key, KeyState), @@ -786,10 +790,10 @@ impl ApplicationHandler for App let window_size = window.inner_size(); - if let Err(err) = window.set_cursor_position(PhysicalPosition { - x: window_size.width / 2, - y: window_size.height / 2, - }) { + if let Err(err) = window.set_cursor_position(Position::Physical(PhysicalPosition { + x: window_size.width as i32 / 2, + y: window_size.height as i32 / 2, + })) { cold_path(); tracing::error!( window_id=?window_id, diff --git a/engine/src/windowing/dpi.rs b/engine/src/windowing/dpi.rs new file mode 100644 index 0000000..2bac619 --- /dev/null +++ b/engine/src/windowing/dpi.rs @@ -0,0 +1,182 @@ +macro_rules! gen_struct_from_to_impls { + ($struct: ident, fields=($($field: ident),*)) => { + impl<Pixel> From<winit::dpi::$struct<Pixel>> for $struct<Pixel> + { + fn from(source: winit::dpi::$struct<Pixel>) -> Self + { + Self { + $($field: source.$field),* + } + } + } + + impl<Pixel> From<$struct<Pixel>> for winit::dpi::$struct<Pixel> + { + fn from(source: $struct<Pixel>) -> Self + { + Self { + $($field: source.$field),* + } + } + } + }; +} + +macro_rules! gen_enum_from_to_impls { + ($enum: ident, variants=($($variant: ident),*)) => { + impl From<winit::dpi::$enum> for $enum + { + fn from(source: winit::dpi::$enum) -> Self + { + match source { + $(winit::dpi::$enum::$variant(pos) => Self::$variant(pos.into())),* + } + } + } + + impl From<$enum> for winit::dpi::$enum + { + fn from(source: $enum) -> Self + { + match source { + $($enum::$variant(pos) => Self::$variant(pos.into())),* + } + } + } + }; +} + +#[derive(Debug, Default, Clone, PartialEq)] +pub struct PhysicalPosition<Pixel> +{ + pub x: Pixel, + pub y: Pixel +} + +impl<Pixel> PhysicalPosition<Pixel> +{ + pub fn to_logical<LogicalPixel>(&self, scale_factor: f64) -> LogicalPosition<LogicalPixel> + where + Pixel: Into<f64> + Clone, + LogicalPixel: From<f64> + { + let x = self.x.clone().into(); + let y = self.y.clone().into(); + + LogicalPosition { + x: (x / scale_factor).into(), + y: (y / scale_factor).into() + } + } + + pub fn try_convert_from<SourcePixel>(source: PhysicalPosition<SourcePixel>) -> Result<Self, Pixel::Error> + where + Pixel: TryFrom<SourcePixel> + { + Ok(Self { + x: source.x.try_into()?, + y: source.y.try_into()? + }) + } +} + +gen_struct_from_to_impls!(PhysicalPosition, fields=(x, y)); + +#[derive(Debug, Default, Clone, PartialEq)] +pub struct LogicalPosition<Pixel> +{ + pub x: Pixel, + pub y: Pixel +} + +impl<Pixel> LogicalPosition<Pixel> +{ + pub fn try_convert_from<SourcePixel>(source: LogicalPosition<SourcePixel>) -> Result<Self, Pixel::Error> + where + Pixel: TryFrom<SourcePixel> + { + Ok(Self { + x: source.x.try_into()?, + y: source.y.try_into()? + }) + } +} + +gen_struct_from_to_impls!(LogicalPosition, fields=(x, y)); + +#[derive(Debug, Clone, PartialEq)] +pub enum Position +{ + Physical(PhysicalPosition<i32>), + Logical(LogicalPosition<f64>) +} + +gen_enum_from_to_impls!(Position, variants=(Physical, Logical)); + +#[derive(Debug, Default, Clone, PartialEq)] +pub struct PhysicalSize<Pixel> +{ + pub width: Pixel, + pub height: Pixel +} + +impl<Pixel> PhysicalSize<Pixel> +{ + pub fn to_logical<LogicalPixel>(&self, scale_factor: f64) -> LogicalSize<LogicalPixel> + where + Pixel: Into<f64> + Clone, + LogicalPixel: From<f64> + { + let width = self.width.clone().into(); + let height = self.height.clone().into(); + + LogicalSize { + width: (width / scale_factor).into(), + height: (height / scale_factor).into() + } + } +} + +impl<Pixel> PhysicalSize<Pixel> +{ + pub fn try_convert_from<SourcePixel>(source: PhysicalSize<SourcePixel>) -> Result<Self, Pixel::Error> + where + Pixel: TryFrom<SourcePixel> + { + Ok(Self { + width: source.width.try_into()?, + height: source.height.try_into()? + }) + } +} + +gen_struct_from_to_impls!(PhysicalSize, fields=(width, height)); + +#[derive(Debug, Default, Clone, PartialEq)] +pub struct LogicalSize<Pixel> +{ + pub width: Pixel, + pub height: Pixel +} + +impl<Pixel> LogicalSize<Pixel> +{ + pub fn try_convert_from<SourcePixel>(source: LogicalSize<SourcePixel>) -> Result<Self, Pixel::Error> + where + Pixel: TryFrom<SourcePixel> + { + Ok(Self { + width: source.width.try_into()?, + height: source.height.try_into()? + }) + } +} + +gen_struct_from_to_impls!(LogicalSize, fields=(width, height)); + +#[derive(Debug, Clone, PartialEq)] +pub enum Size +{ + Physical(PhysicalSize<u32>), + Logical(LogicalSize<f64>) +} diff --git a/engine/src/windowing/mouse.rs b/engine/src/windowing/mouse.rs index f4969ea..649036a 100644 --- a/engine/src/windowing/mouse.rs +++ b/engine/src/windowing/mouse.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use crate::ecs::Sole; use crate::vector::Vec2; +use crate::windowing::dpi::PhysicalPosition; #[derive(Debug, Default, Clone, Sole)] #[non_exhaustive] @@ -15,7 +16,7 @@ pub struct Mouse /// Coordinates in pixels relative to the top-left corner of the window. May have /// been affected by cursor acceleration - pub position: Vec2<f64>, + pub position: PhysicalPosition<f64>, } /// Mouse buttons. diff --git a/engine/src/windowing/window.rs b/engine/src/windowing/window.rs index 7be2562..039d98a 100644 --- a/engine/src/windowing/window.rs +++ b/engine/src/windowing/window.rs @@ -1,8 +1,8 @@ use std::borrow::Cow; -use crate::data_types::dimens::Dimens; use crate::ecs::Component; use crate::reflection::Reflection; +use crate::windowing::dpi::PhysicalSize; pub mod platform; @@ -99,7 +99,7 @@ pub struct Window pub title: String, pub cursor_visible: bool, pub cursor_grab_mode: CursorGrabMode, - pub inner_size: Dimens<u32>, + pub inner_size: PhysicalSize<u32>, wid: Id, scale_factor: f64, } @@ -137,15 +137,15 @@ impl Window winit_window.set_title(&self.title); winit_window.set_cursor_visible(self.cursor_visible); - let curr_inner_size = winit_window.inner_size(); + let curr_inner_size = winit_window.inner_size().clone().into(); let inner_size_request_result = match winit_window - .request_inner_size(winit::dpi::Size::Physical(self.inner_size.into())) + .request_inner_size(winit::dpi::Size::Physical(self.inner_size.clone().into())) { // The comparison of curr_inner_size is in case the user's windowing system // lies about using the requested inner size - None if curr_inner_size == self.inner_size.into() => Ok(()), - None => Err(curr_inner_size.into()), + None if curr_inner_size == self.inner_size => Ok(()), + None => Err(curr_inner_size), Some(inner_size) => Err(inner_size.into()), }; @@ -154,11 +154,6 @@ impl Window } } - pub(crate) fn set_inner_size(&mut self, inner_size: Dimens<u32>) - { - self.inner_size = inner_size; - } - pub(crate) fn set_scale_factor(&mut self, scale_factor: f64) { self.scale_factor = scale_factor; @@ -184,30 +179,8 @@ pub enum CursorGrabMode /// A unique identifier for an X11 visual. pub type XVisualID = u32; -impl<P> From<winit::dpi::PhysicalSize<P>> for Dimens<P> -{ - fn from(size: winit::dpi::PhysicalSize<P>) -> Self - { - Self { - width: size.width, - height: size.height, - } - } -} - -impl<P> From<Dimens<P>> for winit::dpi::PhysicalSize<P> -{ - fn from(dimens: Dimens<P>) -> Self - { - Self { - width: dimens.width, - height: dimens.height, - } - } -} - #[derive(Debug)] pub(crate) struct ApplyResults { - pub inner_size_request: Result<(), Dimens<u32>>, + pub inner_size_request: Result<(), PhysicalSize<u32>>, } |
