From e41f0eabb1d0bd4583dce8035e86abaf38427a99 Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 26 Feb 2024 14:52:00 +0100 Subject: [PATCH] Split `dpi` module out into a separate crate (#3518) Co-authored-by: John Nunley --- CHANGELOG.md | 1 + Cargo.toml | 35 ++-- dpi/Cargo.toml | 39 +++++ src/dpi.rs => dpi/src/lib.rs | 295 +++++++++++++------------------- run-wasm/Cargo.toml | 8 +- src/event.rs | 2 +- src/lib.rs | 24 ++- src/monitor.rs | 2 +- src/platform_impl/ios/window.rs | 4 +- src/window.rs | 49 +++++- 10 files changed, 253 insertions(+), 206 deletions(-) create mode 100644 dpi/Cargo.toml rename src/dpi.rs => dpi/src/lib.rs (66%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c3c548b2c..3d1b482397 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Unreleased` header. # Unreleased +- Move `dpi` types to its own crate, and re-export it from the root crate. - On Wayland, fix DeviceEvent::Motion not being sent - On X11, don't require XIM to run. - On X11, fix xkb state not being updated correctly sometimes leading to wrong input. diff --git a/Cargo.toml b/Cargo.toml index f59ec09d12..c0c143601a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,14 +3,14 @@ name = "winit" version = "0.29.10" authors = ["The winit contributors", "Pierre Krieger "] description = "Cross-platform window creation library." -edition = "2021" keywords = ["windowing"] -license = "Apache-2.0" readme = "README.md" -repository = "https://github.com/rust-windowing/winit" documentation = "https://docs.rs/winit" categories = ["gui"] -rust-version = "1.70.0" +rust-version.workspace = true +repository.workspace = true +license.workspace = true +edition.workspace = true [package.metadata.docs.rs] features = [ @@ -22,7 +22,6 @@ features = [ # Enabled to get docs to compile "android-native-activity", ] -default-target = "x86_64-unknown-linux-gnu" # These are all tested in CI targets = [ # Windows @@ -53,8 +52,8 @@ wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"] wayland-csd-adwaita-notitle = ["sctk-adwaita"] android-native-activity = ["android-activity/native-activity"] android-game-activity = ["android-activity/game-activity"] -serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde"] -mint = ["dep:mint"] +serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde", "dpi/serde"] +mint = ["dpi/mint"] rwh_04 = ["dep:rwh_04", "ndk/rwh_04"] rwh_05 = ["dep:rwh_05", "ndk/rwh_05"] rwh_06 = ["dep:rwh_06", "ndk/rwh_06"] @@ -66,12 +65,12 @@ cfg_aliases = "0.2.0" bitflags = "2" cursor-icon = "1.1.0" log = "0.4" -mint = { version = "0.5.6", optional = true } rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true } rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = ["std"], optional = true } rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true } -serde = { version = "1", optional = true, features = ["serde_derive"] } +serde = { workspace = true, optional = true } smol_str = "0.2.0" +dpi = { path = "dpi" } [dev-dependencies] image = { version = "0.24.0", default-features = false, features = ["png"] } @@ -256,11 +255,23 @@ concurrent-queue = { version = "2", default-features = false } console_log = "1" web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] } +[[example]] +doc-scrape-examples = true +name = "window" + [workspace] +resolver = "2" members = [ + "dpi", "run-wasm", ] -[[example]] -doc-scrape-examples = true -name = "window" +[workspace.package] +rust-version = "1.70.0" +repository = "https://github.com/rust-windowing/winit" +license = "Apache-2.0" +edition = "2021" + +[workspace.dependencies] +serde = { version = "1", features = ["serde_derive"] } +mint = "0.5.6" diff --git a/dpi/Cargo.toml b/dpi/Cargo.toml new file mode 100644 index 0000000000..115fa75859 --- /dev/null +++ b/dpi/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "dpi" +version = "0.0.0" +description = "Types for handling UI scaling" +keywords = ["DPI", "HiDPI", "scale-factor"] +categories = ["gui"] +rust-version.workspace = true +repository.workspace = true +license.workspace = true +edition.workspace = true + +[features] +serde = ["dep:serde"] +mint = ["dep:mint"] + +[dependencies] +serde = { workspace = true, optional = true } +mint = { workspace = true, optional = true } + +[package.metadata.docs.rs] +features = ["serde", "mint"] +# These are all tested in CI +targets = [ + # Windows + "i686-pc-windows-msvc", + "x86_64-pc-windows-msvc", + # macOS + "x86_64-apple-darwin", + # Unix (X11 & Wayland) + "i686-unknown-linux-gnu", + "x86_64-unknown-linux-gnu", + # iOS + "x86_64-apple-ios", + # Android + "aarch64-linux-android", + # Web + "wasm32-unknown-unknown", +] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/src/dpi.rs b/dpi/src/lib.rs similarity index 66% rename from src/dpi.rs rename to dpi/src/lib.rs index 3d7b7b2395..aeb1038a88 100644 --- a/src/dpi.rs +++ b/dpi/src/lib.rs @@ -1,4 +1,4 @@ -//! UI scaling is important, so read the docs for this module if you don't want to be confused. +//! # DPI //! //! ## Why should I care about UI scaling? //! @@ -35,14 +35,11 @@ //! //! ### Position and Size types //! -//! Winit's [`PhysicalPosition`] / [`PhysicalSize`] types correspond with the actual pixels on the +//! The [`PhysicalPosition`] / [`PhysicalSize`] types correspond with the actual pixels on the //! device, and the [`LogicalPosition`] / [`LogicalSize`] types correspond to the physical pixels //! divided by the scale factor. -//! All of Winit's functions return physical types, but can take either logical or physical -//! coordinates as input, allowing you to use the most convenient coordinate system for your -//! particular application. //! -//! Winit's position and size types are generic over their exact pixel type, `P`, to allow the +//! The position and size types are generic over their exact pixel type, `P`, to allow the //! API to have integer precision where appropriate (e.g. most window manipulation functions) and //! floating precision when necessary (e.g. logical sizes for fractional scale factors and touch //! input). If `P` is a floating-point type, please do not cast the values with `as {int}`. Doing so @@ -51,58 +48,24 @@ //! rounding properly. Note that precision loss will still occur when rounding from a float to an //! int, although rounding lessens the problem. //! -//! ### Events +//! ## Cargo Features //! -//! Winit will dispatch a [`ScaleFactorChanged`] event whenever a window's scale factor has changed. -//! This can happen if the user drags their window from a standard-resolution monitor to a high-DPI -//! monitor or if the user changes their DPI settings. This allows you to rescale your application's -//! UI elements and adjust how the platform changes the window's size to reflect the new scale -//! factor. If a window hasn't received a [`ScaleFactorChanged`] event, its scale factor -//! can be found by calling [`window.scale_factor()`]. +//! This crate provides the following Cargo features: //! -//! ## How is the scale factor calculated? -//! -//! The scale factor is calculated differently on different platforms: -//! -//! - **Windows:** On Windows 8 and 10, per-monitor scaling is readily configured by users from the -//! display settings. While users are free to select any option they want, they're only given a -//! selection of "nice" scale factors, i.e. 1.0, 1.25, 1.5... on Windows 7. The scale factor is -//! global and changing it requires logging out. See [this article][windows_1] for technical -//! details. -//! - **macOS:** Recent macOS versions allow the user to change the scaling factor for specific -//! displays. When available, the user may pick a per-monitor scaling factor from a set of -//! pre-defined settings. All "retina displays" have a scaling factor above 1.0 by default, -//! but the specific value varies across devices. -//! - **X11:** Many man-hours have been spent trying to figure out how to handle DPI in X11. Winit -//! currently uses a three-pronged approach: -//! + Use the value in the `WINIT_X11_SCALE_FACTOR` environment variable if present. -//! + If not present, use the value set in `Xft.dpi` in Xresources. -//! + Otherwise, calculate the scale factor based on the millimeter monitor dimensions provided by XRandR. -//! -//! If `WINIT_X11_SCALE_FACTOR` is set to `randr`, it'll ignore the `Xft.dpi` field and use the -//! XRandR scaling method. Generally speaking, you should try to configure the standard system -//! variables to do what you want before resorting to `WINIT_X11_SCALE_FACTOR`. -//! - **Wayland:** The scale factor is suggested by the compositor for each window individually. The -//! monitor scale factor may differ from the window scale factor. -//! - **iOS:** Scale factors are set by Apple to the value that best suits the device, and range -//! from `1.0` to `3.0`. See [this article][apple_1] and [this article][apple_2] for more -//! information. -//! - **Android:** Scale factors are set by the manufacturer to the value that best suits the -//! device, and range from `1.0` to `4.0`. See [this article][android_1] for more information. -//! - **Web:** The scale factor is the ratio between CSS pixels and the physical device pixels. -//! In other words, it is the value of [`window.devicePixelRatio`][web_1]. It is affected by -//! both the screen scaling and the browser zoom level and can go below `1.0`. +//! * `serde`: Enables serialization/deserialization of certain types with +//! [Serde](https://crates.io/crates/serde). +//! * `mint`: Enables mint (math interoperability standard types) conversions. //! //! //! [points]: https://en.wikipedia.org/wiki/Point_(typography) //! [picas]: https://en.wikipedia.org/wiki/Pica_(typography) -//! [`ScaleFactorChanged`]: crate::event::WindowEvent::ScaleFactorChanged -//! [`window.scale_factor()`]: crate::window::Window::scale_factor -//! [windows_1]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows -//! [apple_1]: https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html -//! [apple_2]: https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/ -//! [android_1]: https://developer.android.com/training/multiscreen/screendensities -//! [web_1]: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio + +#![cfg_attr( + docsrs, + feature(doc_auto_cfg, doc_cfg_hide), + doc(cfg_hide(doc, docsrs)) +)] +#![forbid(unsafe_code)] #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -587,15 +550,13 @@ impl From> for Position { #[cfg(test)] mod tests { - use crate::dpi; + use super::*; use std::collections::HashSet; macro_rules! test_pixel_int_impl { ($($name:ident => $ty:ty),*) => {$( #[test] fn $name() { - use dpi::Pixel; - assert_eq!( <$ty as Pixel>::from_f64(37.0), 37, @@ -664,8 +625,6 @@ mod tests { ($($name:ident => $ty:ty),*) => {$( #[test] fn $name() { - use dpi::Pixel; - assert_approx_eq!( <$ty as Pixel>::from_f64(37.0), 37.0, @@ -758,46 +717,40 @@ mod tests { #[test] fn test_validate_scale_factor() { - assert!(dpi::validate_scale_factor(1.0)); - assert!(dpi::validate_scale_factor(2.0)); - assert!(dpi::validate_scale_factor(3.0)); - assert!(dpi::validate_scale_factor(1.5)); - assert!(dpi::validate_scale_factor(0.5)); + assert!(validate_scale_factor(1.0)); + assert!(validate_scale_factor(2.0)); + assert!(validate_scale_factor(3.0)); + assert!(validate_scale_factor(1.5)); + assert!(validate_scale_factor(0.5)); - assert!(!dpi::validate_scale_factor(0.0)); - assert!(!dpi::validate_scale_factor(-1.0)); - assert!(!dpi::validate_scale_factor(f64::INFINITY)); - assert!(!dpi::validate_scale_factor(f64::NAN)); - assert!(!dpi::validate_scale_factor(f64::NEG_INFINITY)); + assert!(!validate_scale_factor(0.0)); + assert!(!validate_scale_factor(-1.0)); + assert!(!validate_scale_factor(f64::INFINITY)); + assert!(!validate_scale_factor(f64::NAN)); + assert!(!validate_scale_factor(f64::NEG_INFINITY)); } #[test] fn test_logical_position() { - let log_pos = dpi::LogicalPosition::new(1.0, 2.0); - assert_eq!( - log_pos.to_physical::(1.0), - dpi::PhysicalPosition::new(1, 2) - ); - assert_eq!( - log_pos.to_physical::(2.0), - dpi::PhysicalPosition::new(2, 4) - ); - assert_eq!(log_pos.cast::(), dpi::LogicalPosition::new(1, 2)); + let log_pos = LogicalPosition::new(1.0, 2.0); + assert_eq!(log_pos.to_physical::(1.0), PhysicalPosition::new(1, 2)); + assert_eq!(log_pos.to_physical::(2.0), PhysicalPosition::new(2, 4)); + assert_eq!(log_pos.cast::(), LogicalPosition::new(1, 2)); assert_eq!( log_pos, - dpi::LogicalPosition::from_physical(dpi::PhysicalPosition::new(1.0, 2.0), 1.0) + LogicalPosition::from_physical(PhysicalPosition::new(1.0, 2.0), 1.0) ); assert_eq!( log_pos, - dpi::LogicalPosition::from_physical(dpi::PhysicalPosition::new(2.0, 4.0), 2.0) + LogicalPosition::from_physical(PhysicalPosition::new(2.0, 4.0), 2.0) ); assert_eq!( - dpi::LogicalPosition::from((2.0, 2.0)), - dpi::LogicalPosition::new(2.0, 2.0) + LogicalPosition::from((2.0, 2.0)), + LogicalPosition::new(2.0, 2.0) ); assert_eq!( - dpi::LogicalPosition::from([2.0, 3.0]), - dpi::LogicalPosition::new(2.0, 3.0) + LogicalPosition::from([2.0, 3.0]), + LogicalPosition::new(2.0, 3.0) ); let x: (f64, f64) = log_pos.into(); @@ -809,56 +762,44 @@ mod tests { #[test] fn test_physical_position() { assert_eq!( - dpi::PhysicalPosition::from_logical(dpi::LogicalPosition::new(1.0, 2.0), 1.0), - dpi::PhysicalPosition::new(1, 2) + PhysicalPosition::from_logical(LogicalPosition::new(1.0, 2.0), 1.0), + PhysicalPosition::new(1, 2) ); assert_eq!( - dpi::PhysicalPosition::from_logical(dpi::LogicalPosition::new(2.0, 4.0), 0.5), - dpi::PhysicalPosition::new(1, 2) + PhysicalPosition::from_logical(LogicalPosition::new(2.0, 4.0), 0.5), + PhysicalPosition::new(1, 2) ); assert_eq!( - dpi::PhysicalPosition::from((2.0, 2.0)), - dpi::PhysicalPosition::new(2.0, 2.0) + PhysicalPosition::from((2.0, 2.0)), + PhysicalPosition::new(2.0, 2.0) ); assert_eq!( - dpi::PhysicalPosition::from([2.0, 3.0]), - dpi::PhysicalPosition::new(2.0, 3.0) + PhysicalPosition::from([2.0, 3.0]), + PhysicalPosition::new(2.0, 3.0) ); - let x: (f64, f64) = dpi::PhysicalPosition::new(1, 2).into(); + let x: (f64, f64) = PhysicalPosition::new(1, 2).into(); assert_eq!(x, (1.0, 2.0)); - let x: [f64; 2] = dpi::PhysicalPosition::new(1, 2).into(); + let x: [f64; 2] = PhysicalPosition::new(1, 2).into(); assert_eq!(x, [1.0, 2.0]); } #[test] fn test_logical_size() { - let log_size = dpi::LogicalSize::new(1.0, 2.0); - assert_eq!( - log_size.to_physical::(1.0), - dpi::PhysicalSize::new(1, 2) - ); - assert_eq!( - log_size.to_physical::(2.0), - dpi::PhysicalSize::new(2, 4) - ); - assert_eq!(log_size.cast::(), dpi::LogicalSize::new(1, 2)); + let log_size = LogicalSize::new(1.0, 2.0); + assert_eq!(log_size.to_physical::(1.0), PhysicalSize::new(1, 2)); + assert_eq!(log_size.to_physical::(2.0), PhysicalSize::new(2, 4)); + assert_eq!(log_size.cast::(), LogicalSize::new(1, 2)); assert_eq!( log_size, - dpi::LogicalSize::from_physical(dpi::PhysicalSize::new(1.0, 2.0), 1.0) + LogicalSize::from_physical(PhysicalSize::new(1.0, 2.0), 1.0) ); assert_eq!( log_size, - dpi::LogicalSize::from_physical(dpi::PhysicalSize::new(2.0, 4.0), 2.0) - ); - assert_eq!( - dpi::LogicalSize::from((2.0, 2.0)), - dpi::LogicalSize::new(2.0, 2.0) - ); - assert_eq!( - dpi::LogicalSize::from([2.0, 3.0]), - dpi::LogicalSize::new(2.0, 3.0) + LogicalSize::from_physical(PhysicalSize::new(2.0, 4.0), 2.0) ); + assert_eq!(LogicalSize::from((2.0, 2.0)), LogicalSize::new(2.0, 2.0)); + assert_eq!(LogicalSize::from([2.0, 3.0]), LogicalSize::new(2.0, 3.0)); let x: (f64, f64) = log_size.into(); assert_eq!(x, (1.0, 2.0)); @@ -869,136 +810,130 @@ mod tests { #[test] fn test_physical_size() { assert_eq!( - dpi::PhysicalSize::from_logical(dpi::LogicalSize::new(1.0, 2.0), 1.0), - dpi::PhysicalSize::new(1, 2) - ); - assert_eq!( - dpi::PhysicalSize::from_logical(dpi::LogicalSize::new(2.0, 4.0), 0.5), - dpi::PhysicalSize::new(1, 2) - ); - assert_eq!( - dpi::PhysicalSize::from((2.0, 2.0)), - dpi::PhysicalSize::new(2.0, 2.0) + PhysicalSize::from_logical(LogicalSize::new(1.0, 2.0), 1.0), + PhysicalSize::new(1, 2) ); assert_eq!( - dpi::PhysicalSize::from([2.0, 3.0]), - dpi::PhysicalSize::new(2.0, 3.0) + PhysicalSize::from_logical(LogicalSize::new(2.0, 4.0), 0.5), + PhysicalSize::new(1, 2) ); + assert_eq!(PhysicalSize::from((2.0, 2.0)), PhysicalSize::new(2.0, 2.0)); + assert_eq!(PhysicalSize::from([2.0, 3.0]), PhysicalSize::new(2.0, 3.0)); - let x: (f64, f64) = dpi::PhysicalSize::new(1, 2).into(); + let x: (f64, f64) = PhysicalSize::new(1, 2).into(); assert_eq!(x, (1.0, 2.0)); - let x: [f64; 2] = dpi::PhysicalSize::new(1, 2).into(); + let x: [f64; 2] = PhysicalSize::new(1, 2).into(); assert_eq!(x, [1.0, 2.0]); } #[test] fn test_size() { assert_eq!( - dpi::Size::new(dpi::PhysicalSize::new(1, 2)), - dpi::Size::Physical(dpi::PhysicalSize::new(1, 2)) + Size::new(PhysicalSize::new(1, 2)), + Size::Physical(PhysicalSize::new(1, 2)) ); assert_eq!( - dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)), - dpi::Size::Logical(dpi::LogicalSize::new(1.0, 2.0)) + Size::new(LogicalSize::new(1.0, 2.0)), + Size::Logical(LogicalSize::new(1.0, 2.0)) ); assert_eq!( - dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_logical::(1.0), - dpi::LogicalSize::new(1.0, 2.0) + Size::new(PhysicalSize::new(1, 2)).to_logical::(1.0), + LogicalSize::new(1.0, 2.0) ); assert_eq!( - dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_logical::(2.0), - dpi::LogicalSize::new(0.5, 1.0) + Size::new(PhysicalSize::new(1, 2)).to_logical::(2.0), + LogicalSize::new(0.5, 1.0) ); assert_eq!( - dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)).to_logical::(1.0), - dpi::LogicalSize::new(1.0, 2.0) + Size::new(LogicalSize::new(1.0, 2.0)).to_logical::(1.0), + LogicalSize::new(1.0, 2.0) ); assert_eq!( - dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_physical::(1.0), - dpi::PhysicalSize::new(1, 2) + Size::new(PhysicalSize::new(1, 2)).to_physical::(1.0), + PhysicalSize::new(1, 2) ); assert_eq!( - dpi::Size::new(dpi::PhysicalSize::new(1, 2)).to_physical::(2.0), - dpi::PhysicalSize::new(1, 2) + Size::new(PhysicalSize::new(1, 2)).to_physical::(2.0), + PhysicalSize::new(1, 2) ); assert_eq!( - dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)).to_physical::(1.0), - dpi::PhysicalSize::new(1, 2) + Size::new(LogicalSize::new(1.0, 2.0)).to_physical::(1.0), + PhysicalSize::new(1, 2) ); assert_eq!( - dpi::Size::new(dpi::LogicalSize::new(1.0, 2.0)).to_physical::(2.0), - dpi::PhysicalSize::new(2, 4) + Size::new(LogicalSize::new(1.0, 2.0)).to_physical::(2.0), + PhysicalSize::new(2, 4) ); - let small = dpi::Size::Physical((1, 2).into()); - let medium = dpi::Size::Logical((3, 4).into()); - let medium_physical = dpi::Size::new(medium.to_physical::(1.0)); - let large = dpi::Size::Physical((5, 6).into()); - assert_eq!(dpi::Size::clamp(medium, small, large, 1.0), medium_physical); - assert_eq!(dpi::Size::clamp(small, medium, large, 1.0), medium_physical); - assert_eq!(dpi::Size::clamp(large, small, medium, 1.0), medium_physical); + let small = Size::Physical((1, 2).into()); + let medium = Size::Logical((3, 4).into()); + let medium_physical = Size::new(medium.to_physical::(1.0)); + let large = Size::Physical((5, 6).into()); + assert_eq!(Size::clamp(medium, small, large, 1.0), medium_physical); + assert_eq!(Size::clamp(small, medium, large, 1.0), medium_physical); + assert_eq!(Size::clamp(large, small, medium, 1.0), medium_physical); } #[test] fn test_position() { assert_eq!( - dpi::Position::new(dpi::PhysicalPosition::new(1, 2)), - dpi::Position::Physical(dpi::PhysicalPosition::new(1, 2)) + Position::new(PhysicalPosition::new(1, 2)), + Position::Physical(PhysicalPosition::new(1, 2)) ); assert_eq!( - dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)), - dpi::Position::Logical(dpi::LogicalPosition::new(1.0, 2.0)) + Position::new(LogicalPosition::new(1.0, 2.0)), + Position::Logical(LogicalPosition::new(1.0, 2.0)) ); assert_eq!( - dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_logical::(1.0), - dpi::LogicalPosition::new(1.0, 2.0) + Position::new(PhysicalPosition::new(1, 2)).to_logical::(1.0), + LogicalPosition::new(1.0, 2.0) ); assert_eq!( - dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_logical::(2.0), - dpi::LogicalPosition::new(0.5, 1.0) + Position::new(PhysicalPosition::new(1, 2)).to_logical::(2.0), + LogicalPosition::new(0.5, 1.0) ); assert_eq!( - dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)).to_logical::(1.0), - dpi::LogicalPosition::new(1.0, 2.0) + Position::new(LogicalPosition::new(1.0, 2.0)).to_logical::(1.0), + LogicalPosition::new(1.0, 2.0) ); assert_eq!( - dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_physical::(1.0), - dpi::PhysicalPosition::new(1, 2) + Position::new(PhysicalPosition::new(1, 2)).to_physical::(1.0), + PhysicalPosition::new(1, 2) ); assert_eq!( - dpi::Position::new(dpi::PhysicalPosition::new(1, 2)).to_physical::(2.0), - dpi::PhysicalPosition::new(1, 2) + Position::new(PhysicalPosition::new(1, 2)).to_physical::(2.0), + PhysicalPosition::new(1, 2) ); assert_eq!( - dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)).to_physical::(1.0), - dpi::PhysicalPosition::new(1, 2) + Position::new(LogicalPosition::new(1.0, 2.0)).to_physical::(1.0), + PhysicalPosition::new(1, 2) ); assert_eq!( - dpi::Position::new(dpi::LogicalPosition::new(1.0, 2.0)).to_physical::(2.0), - dpi::PhysicalPosition::new(2, 4) + Position::new(LogicalPosition::new(1.0, 2.0)).to_physical::(2.0), + PhysicalPosition::new(2, 4) ); } // Eat coverage for the Debug impls et al #[test] fn ensure_attrs_do_not_panic() { - let _ = format!("{:?}", dpi::LogicalPosition::::default().clone()); - HashSet::new().insert(dpi::LogicalPosition::::default()); + let _ = format!("{:?}", LogicalPosition::::default().clone()); + HashSet::new().insert(LogicalPosition::::default()); - let _ = format!("{:?}", dpi::PhysicalPosition::::default().clone()); - HashSet::new().insert(dpi::PhysicalPosition::::default()); + let _ = format!("{:?}", PhysicalPosition::::default().clone()); + HashSet::new().insert(PhysicalPosition::::default()); - let _ = format!("{:?}", dpi::LogicalSize::::default().clone()); - HashSet::new().insert(dpi::LogicalSize::::default()); + let _ = format!("{:?}", LogicalSize::::default().clone()); + HashSet::new().insert(LogicalSize::::default()); - let _ = format!("{:?}", dpi::PhysicalSize::::default().clone()); - HashSet::new().insert(dpi::PhysicalSize::::default()); + let _ = format!("{:?}", PhysicalSize::::default().clone()); + HashSet::new().insert(PhysicalSize::::default()); - let _ = format!("{:?}", dpi::Size::Physical((1, 2).into()).clone()); - let _ = format!("{:?}", dpi::Position::Physical((1, 2).into()).clone()); + let _ = format!("{:?}", Size::Physical((1, 2).into()).clone()); + let _ = format!("{:?}", Position::Physical((1, 2).into()).clone()); } } diff --git a/run-wasm/Cargo.toml b/run-wasm/Cargo.toml index 3ac77b26d8..ce7e831e31 100644 --- a/run-wasm/Cargo.toml +++ b/run-wasm/Cargo.toml @@ -1,9 +1,11 @@ [package] name = "run-wasm" version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +rust-version.workspace = true +repository.workspace = true +license.workspace = true +edition.workspace = true +publish = false [dependencies] cargo-run-wasm = "0.2.0" diff --git a/src/event.rs b/src/event.rs index fbd55610d7..f069cc181b 100644 --- a/src/event.rs +++ b/src/event.rs @@ -539,7 +539,7 @@ pub enum WindowEvent { /// To update the window size, use the provided [`InnerSizeWriter`] handle. By default, the window is /// resized to the value suggested by the OS, but it can be changed to any value. /// - /// For more information about DPI in general, see the [`dpi`](crate::dpi) module. + /// For more information about DPI in general, see the [`dpi`] crate. ScaleFactorChanged { scale_factor: f64, /// Handle to update inner size during scale changes. diff --git a/src/lib.rs b/src/lib.rs index 8238146f32..d019cff3fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,6 +126,25 @@ //! [`visible` set to `false`](crate::window::WindowAttributes::with_visible) and explicitly make the //! window visible only once you're ready to render into it. //! +//! # UI scaling +//! +//! UI scaling is important, go read the docs for the [`dpi`] crate for an +//! introduction. +//! +//! All of Winit's functions return physical types, but can take either logical or physical +//! coordinates as input, allowing you to use the most convenient coordinate system for your +//! particular application. +//! +//! Winit will dispatch a [`ScaleFactorChanged`] event whenever a window's scale factor has changed. +//! This can happen if the user drags their window from a standard-resolution monitor to a high-DPI +//! monitor or if the user changes their DPI settings. This allows you to rescale your application's +//! UI elements and adjust how the platform changes the window's size to reflect the new scale +//! factor. If a window hasn't received a [`ScaleFactorChanged`] event, its scale factor +//! can be found by calling [`window.scale_factor()`]. +//! +//! [`ScaleFactorChanged`]: event::WindowEvent::ScaleFactorChanged +//! [`window.scale_factor()`]: window::Window::scale_factor +//! //! # Cargo Features //! //! Winit provides the following Cargo features: @@ -177,7 +196,10 @@ #[cfg(feature = "rwh_06")] pub use rwh_06 as raw_window_handle; -pub mod dpi; +// Re-export DPI types so that users don't have to put it in Cargo.toml. +#[doc(inline)] +pub use dpi; + #[macro_use] pub mod error; mod cursor; diff --git a/src/monitor.rs b/src/monitor.rs index f1df3207d1..f856d601ec 100644 --- a/src/monitor.rs +++ b/src/monitor.rs @@ -145,7 +145,7 @@ impl MonitorHandle { /// Returns the scale factor of the underlying monitor. To map logical pixels to physical /// pixels and vice versa, use [`Window::scale_factor`]. /// - /// See the [`dpi`](crate::dpi) module for more information. + /// See the [`dpi`] module for more information. /// /// ## Platform-specific /// diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 81e88e5635..67577c0a12 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -16,7 +16,7 @@ use super::view::WinitView; use super::view_controller::WinitViewController; use crate::{ cursor::Cursor, - dpi::{self, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, + dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size}, error::{ExternalError, NotSupportedError, OsError as RootOsError}, event::{Event, WindowEvent}, icon::Icon, @@ -537,7 +537,7 @@ impl Window { let screen = window.screen(); let screen_space = screen.coordinateSpace(); let screen_frame = view.convertRect_toCoordinateSpace(bounds, &screen_space); - let size = crate::dpi::LogicalSize { + let size = LogicalSize { width: screen_frame.size.width as f64, height: screen_frame.size.height as f64, }; diff --git a/src/window.rs b/src/window.rs index 95ec1261c2..2e48d5c4b6 100644 --- a/src/window.rs +++ b/src/window.rs @@ -540,17 +540,54 @@ impl Window { /// /// This value may differ from [`MonitorHandle::scale_factor`]. /// - /// See the [`dpi`](crate::dpi) module for more information. + /// See the [`dpi`] crate for more information. /// /// ## Platform-specific /// - /// - **X11:** This respects Xft.dpi, and can be overridden using the `WINIT_X11_SCALE_FACTOR` environment variable. - /// - **Wayland:** Uses the wp-fractional-scale protocol if available. Falls back to integer-scale factors otherwise. - /// - **Android:** Always returns 1.0. - /// - **iOS:** Can only be called on the main thread. Returns the underlying `UIView`'s - /// [`contentScaleFactor`]. + /// The scale factor is calculated differently on different platforms: + /// + /// - **Windows:** On Windows 8 and 10, per-monitor scaling is readily configured by users from the + /// display settings. While users are free to select any option they want, they're only given a + /// selection of "nice" scale factors, i.e. 1.0, 1.25, 1.5... on Windows 7. The scale factor is + /// global and changing it requires logging out. See [this article][windows_1] for technical + /// details. + /// - **macOS:** Recent macOS versions allow the user to change the scaling factor for specific + /// displays. When available, the user may pick a per-monitor scaling factor from a set of + /// pre-defined settings. All "retina displays" have a scaling factor above 1.0 by default, + /// but the specific value varies across devices. + /// - **X11:** Many man-hours have been spent trying to figure out how to handle DPI in X11. Winit + /// currently uses a three-pronged approach: + /// + Use the value in the `WINIT_X11_SCALE_FACTOR` environment variable if present. + /// + If not present, use the value set in `Xft.dpi` in Xresources. + /// + Otherwise, calculate the scale factor based on the millimeter monitor dimensions provided by XRandR. + /// + /// If `WINIT_X11_SCALE_FACTOR` is set to `randr`, it'll ignore the `Xft.dpi` field and use the + /// XRandR scaling method. Generally speaking, you should try to configure the standard system + /// variables to do what you want before resorting to `WINIT_X11_SCALE_FACTOR`. + /// - **Wayland:** The scale factor is suggested by the compositor for each window individually by + /// using the wp-fractional-scale protocol if available. Falls back to integer-scale factors otherwise. + /// + /// The monitor scale factor may differ from the window scale factor. + /// - **iOS:** Scale factors are set by Apple to the value that best suits the device, and range + /// from `1.0` to `3.0`. See [this article][apple_1] and [this article][apple_2] for more + /// information. + /// + /// This uses the underlying `UIView`'s [`contentScaleFactor`]. + /// - **Android:** Scale factors are set by the manufacturer to the value that best suits the + /// device, and range from `1.0` to `4.0`. See [this article][android_1] for more information. + /// + /// This is currently unimplemented, and this function always returns 1.0. + /// - **Web:** The scale factor is the ratio between CSS pixels and the physical device pixels. + /// In other words, it is the value of [`window.devicePixelRatio`][web_1]. It is affected by + /// both the screen scaling and the browser zoom level and can go below `1.0`. + /// - **Orbital:** This is currently unimplemented, and this function always returns 1.0. /// /// [`WindowEvent::ScaleFactorChanged`]: crate::event::WindowEvent::ScaleFactorChanged + /// [windows_1]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows + /// [apple_1]: https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html + /// [apple_2]: https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/ + /// [android_1]: https://developer.android.com/training/multiscreen/screendensities + /// [web_1]: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio /// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc #[inline] pub fn scale_factor(&self) -> f64 {