kms: Try to calculate dpi and scale factor (Take 2)
Co-authored-by: Jeremy Soller <jackpot51@gmail.com>
This commit is contained in:
parent
bad8837d19
commit
8d2cc05f03
1 changed files with 163 additions and 2 deletions
|
|
@ -15,7 +15,7 @@ use smithay::{
|
||||||
egl::{context::ContextPriority, EGLContext, EGLDevice, EGLDisplay},
|
egl::{context::ContextPriority, EGLContext, EGLDevice, EGLDisplay},
|
||||||
session::Session,
|
session::Session,
|
||||||
},
|
},
|
||||||
output::{Mode as OutputMode, Output, PhysicalProperties, Subpixel},
|
output::{Mode as OutputMode, Output, PhysicalProperties, Scale, Subpixel},
|
||||||
reexports::{
|
reexports::{
|
||||||
calloop::{LoopHandle, RegistrationToken},
|
calloop::{LoopHandle, RegistrationToken},
|
||||||
drm::control::{connector, crtc, Device as ControlDevice, ModeTypeFlags},
|
drm::control::{connector, crtc, Device as ControlDevice, ModeTypeFlags},
|
||||||
|
|
@ -625,6 +625,11 @@ fn populate_modes(
|
||||||
else {
|
else {
|
||||||
anyhow::bail!("No mode found");
|
anyhow::bail!("No mode found");
|
||||||
};
|
};
|
||||||
|
let scale = conn_info
|
||||||
|
.size()
|
||||||
|
.map(|size| calculate_scale(conn_info.interface(), size, mode.size()))
|
||||||
|
.unwrap_or(1.0);
|
||||||
|
|
||||||
let refresh_rate = drm_helpers::calculate_refresh_rate(mode);
|
let refresh_rate = drm_helpers::calculate_refresh_rate(mode);
|
||||||
let output_mode = OutputMode {
|
let output_mode = OutputMode {
|
||||||
size: (mode.size().0 as i32, mode.size().1 as i32).into(),
|
size: (mode.size().0 as i32, mode.size().1 as i32).into(),
|
||||||
|
|
@ -644,7 +649,7 @@ fn populate_modes(
|
||||||
Some(output_mode),
|
Some(output_mode),
|
||||||
// TODO: Readout property for monitor rotation
|
// TODO: Readout property for monitor rotation
|
||||||
Some(Transform::Normal),
|
Some(Transform::Normal),
|
||||||
None,
|
Some(Scale::Fractional(scale)),
|
||||||
Some(Point::from((position.0 as i32, position.1 as i32))),
|
Some(Point::from((position.0 as i32, position.1 as i32))),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -657,8 +662,164 @@ fn populate_modes(
|
||||||
mode: ((output_mode.size.w, output_mode.size.h), Some(refresh_rate)),
|
mode: ((output_mode.size.w, output_mode.size.h), Some(refresh_rate)),
|
||||||
position,
|
position,
|
||||||
max_bpc,
|
max_bpc,
|
||||||
|
scale,
|
||||||
..std::mem::take(&mut *output_config)
|
..std::mem::take(&mut *output_config)
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn calculate_scale(
|
||||||
|
interface: connector::Interface,
|
||||||
|
monitor_size_mm: (u32, u32),
|
||||||
|
resolution: (u16, u16),
|
||||||
|
) -> f64 {
|
||||||
|
let (w_mm, h_mm) = monitor_size_mm;
|
||||||
|
let (w_px, h_px) = resolution;
|
||||||
|
if w_mm == 0 || h_mm == 0 {
|
||||||
|
// possibly projector, but could just be some no-brand display
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (w_in, h_in) = (w_mm as f64 / 25.4, h_mm as f64 / 25.4);
|
||||||
|
// due to edid's setting non-sensicle values,
|
||||||
|
// lets be really careful we don't devide by 0 (or non-normal values) when deriving values from size.
|
||||||
|
// (also the size should be positive, but the u16 arguments already force that.)
|
||||||
|
if !w_in.is_normal() || !h_in.is_normal() {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let diag = (w_in.powf(2.) * h_in.powf(2.)).sqrt();
|
||||||
|
let dpi = (w_px as f64 / w_in + h_px as f64 / h_in) / 2.0;
|
||||||
|
let shorter_res = if w_mm < h_mm { w_px } else { h_px };
|
||||||
|
|
||||||
|
match diag {
|
||||||
|
_diag if diag < 20. || interface == connector::Interface::EmbeddedDisplayPort => {
|
||||||
|
// likely laptop
|
||||||
|
scale_from_dpi(dpi, 144, shorter_res)
|
||||||
|
}
|
||||||
|
// 16:10 has a height of 19.6, anything lower is most likely an ultrawide
|
||||||
|
_diag if diag >= 40. && h_in >= 19. => {
|
||||||
|
// likely TV
|
||||||
|
match shorter_res {
|
||||||
|
px if px <= 1200 => 1.0,
|
||||||
|
_ => 2.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// likely desktop
|
||||||
|
scale_from_dpi(dpi, 120, shorter_res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scale_from_dpi(dpi: f64, step_size: u32, shorter_px: u16) -> f64 {
|
||||||
|
let scale = (dpi / step_size as f64) * 100.0;
|
||||||
|
|
||||||
|
// snap to 50%
|
||||||
|
let scale = ((scale.round() as u32).next_multiple_of(50) as f64) / 100.0;
|
||||||
|
|
||||||
|
// max values
|
||||||
|
let max = match shorter_px {
|
||||||
|
px if px <= 1200 => 1.0, // 125% is usually a worse experience than 100% atm
|
||||||
|
// for 1440p and weird variants we let the algorithm take over, but limit the max, this is inspired by what windows does
|
||||||
|
px if px <= 1600 => 1.5,
|
||||||
|
_ => 2.0, // never go higher than 200% by default though (Note for the future: Tablets? Phones?)
|
||||||
|
};
|
||||||
|
|
||||||
|
let min = match shorter_px {
|
||||||
|
px if px >= 2000 => 2.0, // 4k should default to 200%, as we prefer integer scales
|
||||||
|
_ => 1.0, // never go lower than 100%
|
||||||
|
};
|
||||||
|
|
||||||
|
scale.min(max).max(min)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::{calculate_scale, connector::Interface};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_scale() {
|
||||||
|
fn scale(interface: Interface, diag_in: f64, w_px: u16, h_px: u16) -> i32 {
|
||||||
|
let aspect = (w_px as f64) / (h_px as f64);
|
||||||
|
let diag_mm = diag_in * 25.4;
|
||||||
|
let h_mm = diag_mm / (aspect.powf(2.0) + 1.0).sqrt();
|
||||||
|
let w_mm = h_mm * aspect;
|
||||||
|
(calculate_scale(interface, (w_mm as u32, h_mm as u32), (w_px, h_px)) * 100.0) as i32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Laptops
|
||||||
|
for &iface in &[Interface::EmbeddedDisplayPort, Interface::LVDS] {
|
||||||
|
// 14 inch 3:2
|
||||||
|
assert_eq!(scale(iface, 14.0, 3000, 2000), 200);
|
||||||
|
|
||||||
|
// Various sizes 16:9
|
||||||
|
for &diag_in in &[14.0, 15.6, 17.3] {
|
||||||
|
assert_eq!(scale(iface, diag_in, 1920, 1080), 100);
|
||||||
|
assert_eq!(scale(iface, diag_in, 2560, 1440), 150);
|
||||||
|
assert_eq!(scale(iface, diag_in, 3840, 2160), 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Various sizes 16:10
|
||||||
|
for &diag_in in &[14.0, 16.0] {
|
||||||
|
assert_eq!(scale(iface, diag_in, 1920, 1200), 100);
|
||||||
|
assert_eq!(scale(iface, diag_in, 2560, 1600), 150);
|
||||||
|
assert_eq!(scale(iface, diag_in, 3840, 2400), 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Desktops
|
||||||
|
for &iface in &[Interface::DisplayPort, Interface::HDMIA, Interface::HDMIB] {
|
||||||
|
// 24 inch 16:9
|
||||||
|
assert_eq!(scale(iface, 24.0, 1920, 1080), 100);
|
||||||
|
assert_eq!(scale(iface, 24.0, 2560, 1440), 150);
|
||||||
|
assert_eq!(scale(iface, 24.0, 3840, 2160), 200);
|
||||||
|
|
||||||
|
// Larger sizes 16:9
|
||||||
|
for &diag_in in &[27.0, 32.0, 38.0] {
|
||||||
|
assert_eq!(scale(iface, diag_in, 1920, 1080), 100);
|
||||||
|
assert_eq!(scale(iface, diag_in, 2560, 1440), 100);
|
||||||
|
assert_eq!(scale(iface, diag_in, 3840, 2160), 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smaller sizes 21:9
|
||||||
|
for &diag_in in &[26.0, 29.0] {
|
||||||
|
assert_eq!(scale(iface, diag_in, 2560, 1080), 100);
|
||||||
|
assert_eq!(scale(iface, diag_in, 3440, 1440), 150);
|
||||||
|
assert_eq!(scale(iface, diag_in, 5120, 2160), 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Larger sizes 21:9
|
||||||
|
for &diag_in in &[34.0, 45.0] {
|
||||||
|
assert_eq!(scale(iface, diag_in, 2560, 1080), 100);
|
||||||
|
assert_eq!(scale(iface, diag_in, 3440, 1440), 100);
|
||||||
|
assert_eq!(scale(iface, diag_in, 5120, 2160), 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Various sizes 32:9
|
||||||
|
for &diag_in in &[45.0, 49.0, 57.0] {
|
||||||
|
assert_eq!(scale(iface, diag_in, 3840, 1080), 100);
|
||||||
|
assert_eq!(scale(iface, diag_in, 5120, 1440), 100);
|
||||||
|
assert_eq!(scale(iface, diag_in, 7680, 2160), 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TVs
|
||||||
|
for &iface in &[Interface::DisplayPort, Interface::HDMIA, Interface::HDMIB] {
|
||||||
|
// Various sizes 16:9
|
||||||
|
for &diag_in in &[40.0, 42.0, 48.0, 55.0, 65.0, 77.0, 83.0] {
|
||||||
|
assert_eq!(scale(iface, diag_in, 1920, 1080), 100);
|
||||||
|
assert_eq!(scale(iface, diag_in, 2560, 1440), 200);
|
||||||
|
assert_eq!(scale(iface, diag_in, 3840, 2160), 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero sized displays (projectors, invalid EDID)
|
||||||
|
for &iface in &[Interface::DisplayPort, Interface::HDMIA, Interface::HDMIB] {
|
||||||
|
assert_eq!(scale(iface, 0.0, 1920, 1080), 100);
|
||||||
|
assert_eq!(scale(iface, 0.0, 2560, 1440), 100);
|
||||||
|
assert_eq!(scale(iface, 0.0, 3840, 2160), 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue