mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
5 Commits
feat/dpi-s
...
dpi_v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06f4e28de9 | ||
|
|
249d5d8bff | ||
|
|
66df2c22ba | ||
|
|
359a38844b | ||
|
|
563b0bf5e3 |
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@@ -145,6 +145,13 @@ jobs:
|
||||
- name: Build crate
|
||||
run: cargo $CMD build $OPTIONS
|
||||
|
||||
# Test only on Linux x86_64, so we avoid spending unnecessary CI hours.
|
||||
- name: Test dpi crate
|
||||
if: >
|
||||
contains(matrix.platform.name, 'Linux 64bit') &&
|
||||
matrix.toolchain != '1.70.0'
|
||||
run: cargo test -p dpi
|
||||
|
||||
- name: Build tests
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
|
||||
@@ -51,6 +51,7 @@ Unreleased` header.
|
||||
- **Breaking:** Removed `EventLoopBuilder::with_user_event`, the functionality is now available in `EventLoop::with_user_event`.
|
||||
- Add `Window::default_attributes` to get default `WindowAttributes`.
|
||||
- `log` has been replaced with `tracing`. The old behavior can be emulated by setting the `log` feature on the `tracing` crate.
|
||||
- On X11, fix crash due to xsettings query on systems with incomplete xsettings.
|
||||
|
||||
# 0.29.14
|
||||
|
||||
|
||||
16
dpi/CHANGELOG.md
Normal file
16
dpi/CHANGELOG.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
Please keep one empty line before and after all headers. (This is required for
|
||||
`git` to produce a conflict when a release is made while a PR is open and the
|
||||
PR's changelog entry would go into the wrong section).
|
||||
|
||||
And please only add new entries to the top of this list, right below the `#
|
||||
Unreleased` header.
|
||||
|
||||
# Unreleased
|
||||
|
||||
# 0.1.0
|
||||
|
||||
- Add `LogicalUnit`, `PhysicalUnit` and `PixelUnit` types and related functions.
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "dpi"
|
||||
version = "0.0.0"
|
||||
version = "0.1.0"
|
||||
description = "Types for handling UI scaling"
|
||||
keywords = ["DPI", "HiDPI", "scale-factor"]
|
||||
categories = ["gui"]
|
||||
|
||||
273
dpi/src/lib.rs
273
dpi/src/lib.rs
@@ -35,8 +35,8 @@
|
||||
//!
|
||||
//! ### Position and Size types
|
||||
//!
|
||||
//! The [`PhysicalPosition`] / [`PhysicalSize`] types correspond with the actual pixels on the
|
||||
//! device, and the [`LogicalPosition`] / [`LogicalSize`] types correspond to the physical pixels
|
||||
//! The [`PhysicalPosition`] / [`PhysicalSize`] / [`PhysicalUnit`] types correspond with the actual pixels on the
|
||||
//! device, and the [`LogicalPosition`] / [`LogicalSize`] / [`LogicalUnit`] types correspond to the physical pixels
|
||||
//! divided by the scale factor.
|
||||
//!
|
||||
//! The position and size types are generic over their exact pixel type, `P`, to allow the
|
||||
@@ -128,6 +128,238 @@ pub fn validate_scale_factor(scale_factor: f64) -> bool {
|
||||
scale_factor.is_sign_positive() && scale_factor.is_normal()
|
||||
}
|
||||
|
||||
/// A logical pixel unit.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct LogicalUnit<P>(pub P);
|
||||
|
||||
impl<P> LogicalUnit<P> {
|
||||
/// Represents a minimum logical unit of [`f64::MAX`].
|
||||
pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
|
||||
/// Represents a logical unit of `0_f64`.
|
||||
pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0);
|
||||
/// Represents a maximum logical unit that is equal to [`f64::MAX`].
|
||||
pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
|
||||
|
||||
#[inline]
|
||||
pub const fn new(v: P) -> Self {
|
||||
LogicalUnit(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> LogicalUnit<P> {
|
||||
#[inline]
|
||||
pub fn from_physical<T: Into<PhysicalUnit<X>>, X: Pixel>(
|
||||
physical: T,
|
||||
scale_factor: f64,
|
||||
) -> Self {
|
||||
physical.into().to_logical(scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalUnit<X> {
|
||||
assert!(validate_scale_factor(scale_factor));
|
||||
PhysicalUnit::new(self.0.into() * scale_factor).cast()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cast<X: Pixel>(&self) -> LogicalUnit<X> {
|
||||
LogicalUnit(self.0.cast())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> From<X> for LogicalUnit<P> {
|
||||
fn from(v: X) -> LogicalUnit<P> {
|
||||
LogicalUnit::new(v.cast())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<LogicalUnit<P>> for u8 {
|
||||
fn from(v: LogicalUnit<P>) -> u8 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<LogicalUnit<P>> for u16 {
|
||||
fn from(v: LogicalUnit<P>) -> u16 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<LogicalUnit<P>> for u32 {
|
||||
fn from(v: LogicalUnit<P>) -> u32 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<LogicalUnit<P>> for i8 {
|
||||
fn from(v: LogicalUnit<P>) -> i8 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<LogicalUnit<P>> for i16 {
|
||||
fn from(v: LogicalUnit<P>) -> i16 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<LogicalUnit<P>> for i32 {
|
||||
fn from(v: LogicalUnit<P>) -> i32 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<LogicalUnit<P>> for f32 {
|
||||
fn from(v: LogicalUnit<P>) -> f32 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<LogicalUnit<P>> for f64 {
|
||||
fn from(v: LogicalUnit<P>) -> f64 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
/// A physical pixel unit.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct PhysicalUnit<P>(pub P);
|
||||
|
||||
impl<P> PhysicalUnit<P> {
|
||||
/// Represents a minimum physical unit of [`f64::MAX`].
|
||||
pub const MIN: LogicalUnit<f64> = LogicalUnit::new(f64::MIN);
|
||||
/// Represents a physical unit of `0_f64`.
|
||||
pub const ZERO: LogicalUnit<f64> = LogicalUnit::new(0.0);
|
||||
/// Represents a maximum physical unit that is equal to [`f64::MAX`].
|
||||
pub const MAX: LogicalUnit<f64> = LogicalUnit::new(f64::MAX);
|
||||
|
||||
#[inline]
|
||||
pub const fn new(v: P) -> Self {
|
||||
PhysicalUnit(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> PhysicalUnit<P> {
|
||||
#[inline]
|
||||
pub fn from_logical<T: Into<LogicalUnit<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
|
||||
logical.into().to_physical(scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalUnit<X> {
|
||||
assert!(validate_scale_factor(scale_factor));
|
||||
LogicalUnit::new(self.0.into() / scale_factor).cast()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cast<X: Pixel>(&self) -> PhysicalUnit<X> {
|
||||
PhysicalUnit(self.0.cast())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> From<X> for PhysicalUnit<P> {
|
||||
fn from(v: X) -> PhysicalUnit<P> {
|
||||
PhysicalUnit::new(v.cast())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<PhysicalUnit<P>> for u8 {
|
||||
fn from(v: PhysicalUnit<P>) -> u8 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<PhysicalUnit<P>> for u16 {
|
||||
fn from(v: PhysicalUnit<P>) -> u16 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<PhysicalUnit<P>> for u32 {
|
||||
fn from(v: PhysicalUnit<P>) -> u32 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<PhysicalUnit<P>> for i8 {
|
||||
fn from(v: PhysicalUnit<P>) -> i8 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<PhysicalUnit<P>> for i16 {
|
||||
fn from(v: PhysicalUnit<P>) -> i16 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<PhysicalUnit<P>> for i32 {
|
||||
fn from(v: PhysicalUnit<P>) -> i32 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<PhysicalUnit<P>> for f32 {
|
||||
fn from(v: PhysicalUnit<P>) -> f32 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<PhysicalUnit<P>> for f64 {
|
||||
fn from(v: PhysicalUnit<P>) -> f64 {
|
||||
v.0.cast()
|
||||
}
|
||||
}
|
||||
|
||||
/// A pixel unit that's either physical or logical.
|
||||
pub enum PixelUnit {
|
||||
Physical(PhysicalUnit<i32>),
|
||||
Logical(LogicalUnit<f64>),
|
||||
}
|
||||
|
||||
impl PixelUnit {
|
||||
/// Represents a minimum logical unit of [`f64::MAX`].
|
||||
pub const MIN: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MIN));
|
||||
/// Represents a logical unit of `0_f64`.
|
||||
pub const ZERO: PixelUnit = PixelUnit::Logical(LogicalUnit::new(0.0));
|
||||
/// Represents a maximum logical unit that is equal to [`f64::MAX`].
|
||||
pub const MAX: PixelUnit = PixelUnit::Logical(LogicalUnit::new(f64::MAX));
|
||||
|
||||
pub fn new<S: Into<PixelUnit>>(unit: S) -> PixelUnit {
|
||||
unit.into()
|
||||
}
|
||||
|
||||
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalUnit<P> {
|
||||
match *self {
|
||||
PixelUnit::Physical(unit) => unit.to_logical(scale_factor),
|
||||
PixelUnit::Logical(unit) => unit.cast(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalUnit<P> {
|
||||
match *self {
|
||||
PixelUnit::Physical(unit) => unit.cast(),
|
||||
PixelUnit::Logical(unit) => unit.to_physical(scale_factor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<PhysicalUnit<P>> for PixelUnit {
|
||||
#[inline]
|
||||
fn from(unit: PhysicalUnit<P>) -> PixelUnit {
|
||||
PixelUnit::Physical(unit.cast())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<LogicalUnit<P>> for PixelUnit {
|
||||
#[inline]
|
||||
fn from(unit: LogicalUnit<P>) -> PixelUnit {
|
||||
PixelUnit::Logical(unit.cast())
|
||||
}
|
||||
}
|
||||
|
||||
/// A position represented in logical pixels.
|
||||
///
|
||||
/// The position is stored as floats, so please be careful. Casting floats to integers truncates the
|
||||
@@ -730,6 +962,43 @@ mod tests {
|
||||
assert!(!validate_scale_factor(f64::NEG_INFINITY));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_logical_unity() {
|
||||
let log_unit = LogicalUnit::new(1.0);
|
||||
assert_eq!(log_unit.to_physical::<u32>(1.0), PhysicalUnit::new(1));
|
||||
assert_eq!(log_unit.to_physical::<u32>(2.0), PhysicalUnit::new(2));
|
||||
assert_eq!(log_unit.cast::<u32>(), LogicalUnit::new(1));
|
||||
assert_eq!(
|
||||
log_unit,
|
||||
LogicalUnit::from_physical(PhysicalUnit::new(1.0), 1.0)
|
||||
);
|
||||
assert_eq!(
|
||||
log_unit,
|
||||
LogicalUnit::from_physical(PhysicalUnit::new(2.0), 2.0)
|
||||
);
|
||||
assert_eq!(LogicalUnit::from(2.0), LogicalUnit::new(2.0));
|
||||
|
||||
let x: f64 = log_unit.into();
|
||||
assert_eq!(x, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_physical_unit() {
|
||||
assert_eq!(
|
||||
PhysicalUnit::from_logical(LogicalUnit::new(1.0), 1.0),
|
||||
PhysicalUnit::new(1)
|
||||
);
|
||||
assert_eq!(
|
||||
PhysicalUnit::from_logical(LogicalUnit::new(2.0), 0.5),
|
||||
PhysicalUnit::new(1)
|
||||
);
|
||||
assert_eq!(PhysicalUnit::from(2.0), PhysicalUnit::new(2.0,));
|
||||
assert_eq!(PhysicalUnit::from(2.0), PhysicalUnit::new(2.0));
|
||||
|
||||
let x: f64 = PhysicalUnit::new(1).into();
|
||||
assert_eq!(x, 1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_logical_position() {
|
||||
let log_pos = LogicalPosition::new(1.0, 2.0);
|
||||
|
||||
@@ -195,6 +195,7 @@ impl OnlyCursorImageSource {
|
||||
}
|
||||
|
||||
/// Platforms export this directly as `PlatformCustomCursor` if they don't implement caching.
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct OnlyCursorImage(pub(crate) Arc<CursorImage>);
|
||||
|
||||
|
||||
@@ -887,6 +887,9 @@ pub enum X11Error {
|
||||
|
||||
/// Unable to parse xsettings.
|
||||
XsettingsParse(xsettings::ParserError),
|
||||
|
||||
/// Failed to get property.
|
||||
GetProperty(util::GetPropertyError),
|
||||
}
|
||||
|
||||
impl fmt::Display for X11Error {
|
||||
@@ -896,6 +899,7 @@ impl fmt::Display for X11Error {
|
||||
X11Error::Connect(e) => write!(f, "X11 connection error: {}", e),
|
||||
X11Error::Connection(e) => write!(f, "X11 connection error: {}", e),
|
||||
X11Error::XidsExhausted(e) => write!(f, "XID range exhausted: {}", e),
|
||||
X11Error::GetProperty(e) => write!(f, "Failed to get X property {}", e),
|
||||
X11Error::X11(e) => write!(f, "X11 error: {:?}", e),
|
||||
X11Error::UnexpectedNull(s) => write!(f, "Xlib function returned null: {}", s),
|
||||
X11Error::InvalidActivationToken(s) => write!(
|
||||
@@ -988,6 +992,12 @@ impl From<xsettings::ParserError> for X11Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<util::GetPropertyError> for X11Error {
|
||||
fn from(value: util::GetPropertyError) -> Self {
|
||||
Self::GetProperty(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Type alias for a void cookie.
|
||||
type VoidCookie<'a> = x11rb::cookie::VoidCookie<'a, X11rbConnection>;
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ impl XConnection {
|
||||
.get_string("Xft.dpi", "")
|
||||
.and_then(|s| f64::from_str(s).ok())
|
||||
}
|
||||
|
||||
pub fn get_output_info(
|
||||
&self,
|
||||
resources: &monitor::ScreenResources,
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
use super::*;
|
||||
use bytemuck::{NoUninit, Pod};
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bytemuck::{NoUninit, Pod};
|
||||
|
||||
use x11rb::connection::Connection;
|
||||
use x11rb::errors::ReplyError;
|
||||
|
||||
pub type Cardinal = u32;
|
||||
use super::*;
|
||||
|
||||
pub const CARDINAL_SIZE: usize = mem::size_of::<u32>();
|
||||
|
||||
pub type Cardinal = u32;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GetPropertyError {
|
||||
X11rbError(Arc<ReplyError>),
|
||||
@@ -15,12 +20,6 @@ pub enum GetPropertyError {
|
||||
FormatMismatch(c_int),
|
||||
}
|
||||
|
||||
impl<T: Into<ReplyError>> From<T> for GetPropertyError {
|
||||
fn from(e: T) -> Self {
|
||||
Self::X11rbError(Arc::new(e.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl GetPropertyError {
|
||||
pub fn is_actual_property_type(&self, t: xproto::Atom) -> bool {
|
||||
if let GetPropertyError::TypeMismatch(actual_type) = *self {
|
||||
@@ -31,6 +30,24 @@ impl GetPropertyError {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<ReplyError>> From<T> for GetPropertyError {
|
||||
fn from(e: T) -> Self {
|
||||
Self::X11rbError(Arc::new(e.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for GetPropertyError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
GetPropertyError::X11rbError(err) => err.fmt(f),
|
||||
GetPropertyError::TypeMismatch(err) => write!(f, "type mismatch: {err}"),
|
||||
GetPropertyError::FormatMismatch(err) => write!(f, "format mismatch: {err}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for GetPropertyError {}
|
||||
|
||||
// Number of 32-bit chunks to retrieve per iteration of get_property's inner loop.
|
||||
// To test if `get_property` works correctly, set this to 1.
|
||||
const PROPERTY_BUFFER_SIZE: u32 = 1024; // 4k of RAM ought to be enough for anyone!
|
||||
|
||||
@@ -33,13 +33,11 @@ impl XConnection {
|
||||
.reply()?;
|
||||
|
||||
// Read the _XSETTINGS_SETTINGS property.
|
||||
let data: Vec<u8> = self
|
||||
.get_property(
|
||||
owner.owner,
|
||||
atoms[_XSETTINGS_SETTINGS],
|
||||
atoms[_XSETTINGS_SETTINGS],
|
||||
)
|
||||
.unwrap();
|
||||
let data: Vec<u8> = self.get_property(
|
||||
owner.owner,
|
||||
atoms[_XSETTINGS_SETTINGS],
|
||||
atoms[_XSETTINGS_SETTINGS],
|
||||
)?;
|
||||
|
||||
// Parse the property.
|
||||
let dpi_setting = read_settings(&data)?
|
||||
|
||||
Reference in New Issue
Block a user