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},
|
||||
session::Session,
|
||||
},
|
||||
output::{Mode as OutputMode, Output, PhysicalProperties, Subpixel},
|
||||
output::{Mode as OutputMode, Output, PhysicalProperties, Scale, Subpixel},
|
||||
reexports::{
|
||||
calloop::{LoopHandle, RegistrationToken},
|
||||
drm::control::{connector, crtc, Device as ControlDevice, ModeTypeFlags},
|
||||
|
|
@ -625,6 +625,11 @@ fn populate_modes(
|
|||
else {
|
||||
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 output_mode = OutputMode {
|
||||
size: (mode.size().0 as i32, mode.size().1 as i32).into(),
|
||||
|
|
@ -644,7 +649,7 @@ fn populate_modes(
|
|||
Some(output_mode),
|
||||
// TODO: Readout property for monitor rotation
|
||||
Some(Transform::Normal),
|
||||
None,
|
||||
Some(Scale::Fractional(scale)),
|
||||
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)),
|
||||
position,
|
||||
max_bpc,
|
||||
scale,
|
||||
..std::mem::take(&mut *output_config)
|
||||
};
|
||||
|
||||
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