summaryrefslogtreecommitdiff
path: root/engine
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2026-06-28 00:09:06 +0200
committerHampusM <hampus@hampusmat.com>2026-06-30 18:24:47 +0200
commit9df8a4d197e66accb389edb5a5c54117933f157e (patch)
tree2d348fea32df9dd73ec6b961fae63e8ccb60899d /engine
parent4a5e3e612e8b597dd1734afefd01324b74fd99ab (diff)
feat(engine): add windowing DPI utilities
Diffstat (limited to 'engine')
-rw-r--r--engine/src/rendering.rs3
-rw-r--r--engine/src/rendering/backend/opengl.rs21
-rw-r--r--engine/src/shader/default.rs3
-rw-r--r--engine/src/ui/imgui.rs15
-rw-r--r--engine/src/windowing.rs28
-rw-r--r--engine/src/windowing/dpi.rs182
-rw-r--r--engine/src/windowing/mouse.rs3
-rw-r--r--engine/src/windowing/window.rs41
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>>,
}