1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 13:21:38 +03:00

windows: support dpi_by_screen option

refs: #4096
This commit is contained in:
Wez Furlong 2023-08-06 08:43:33 -07:00
parent ef94fc8405
commit b11691dda7
No known key found for this signature in database
GPG Key ID: 7A7F66A31EC9B387
2 changed files with 152 additions and 111 deletions

View File

@ -99,114 +99,8 @@ impl ConnectionOps for Connection {
}
fn screens(&self) -> anyhow::Result<Screens> {
struct Info {
primary: Option<ScreenInfo>,
active: Option<ScreenInfo>,
by_name: HashMap<String, ScreenInfo>,
virtual_rect: ScreenRect,
active_handle: HMONITOR,
friendly_names: HashMap<String, String>,
gdi_to_adapater: HashMap<String, String>,
}
unsafe extern "system" fn callback(
mon: HMONITOR,
_hdc: HDC,
_rect: *mut RECT,
data: LPARAM,
) -> i32 {
let info: &mut Info = &mut *(data as *mut Info);
let mut mi: MONITORINFOEXW = std::mem::zeroed();
mi.cbSize = std::mem::size_of::<MONITORINFOEXW>() as u32;
GetMonitorInfoW(mon, &mut mi as *mut MONITORINFOEXW as *mut MONITORINFO);
let mut devmode: DEVMODEW = std::mem::zeroed();
devmode.dmSize = std::mem::size_of::<DEVMODEW>() as u16;
let max_fps =
if EnumDisplaySettingsW(mi.szDevice.as_ptr(), ENUM_CURRENT_SETTINGS, &mut devmode)
!= 0
&& (devmode.dmFields & DM_DISPLAYFREQUENCY) != 0
&& devmode.dmDisplayFrequency > 1
{
Some(devmode.dmDisplayFrequency as usize)
} else {
None
};
let monitor_name = wstr(&mi.szDevice);
let friendly_name = match info.friendly_names.get(&monitor_name) {
Some(name) => name.to_string(),
None => {
// Fall back to EnumDisplayDevicesW.
// It likely has a terribly generic name like "Generic PnP Monitor".
let mut display_device: DISPLAY_DEVICEW = std::mem::zeroed();
display_device.cb = std::mem::size_of::<DISPLAY_DEVICEW>() as u32;
if EnumDisplayDevicesW(mi.szDevice.as_ptr(), 0, &mut display_device, 0) != 0 {
wstr(&display_device.DeviceString)
} else {
"Unknown".to_string()
}
}
};
let adapter_name = match info.gdi_to_adapater.get(&monitor_name) {
Some(name) => name.to_string(),
None => "Unknown".to_string(),
};
// "\\.\DISPLAY1" -> "DISPLAY1"
let monitor_name = if let Some(name) = monitor_name.strip_prefix("\\\\.\\") {
name.to_string()
} else {
monitor_name
};
let monitor_name = format!("{monitor_name}: {friendly_name} on {adapter_name}");
let screen_info = ScreenInfo {
name: monitor_name.clone(),
rect: euclid::rect(
mi.rcMonitor.left as isize,
mi.rcMonitor.top as isize,
mi.rcMonitor.right as isize - mi.rcMonitor.left as isize,
mi.rcMonitor.bottom as isize - mi.rcMonitor.top as isize,
),
scale: 1.0,
max_fps,
};
info.virtual_rect = info.virtual_rect.union(&screen_info.rect);
if mi.dwFlags & MONITORINFOF_PRIMARY == MONITORINFOF_PRIMARY {
info.primary.replace(screen_info.clone());
}
if mon == info.active_handle {
info.active.replace(screen_info.clone());
}
info.by_name.insert(monitor_name, screen_info);
winapi::shared::ntdef::TRUE.into()
}
let mut info = Info {
primary: None,
active: None,
by_name: HashMap::new(),
virtual_rect: euclid::rect(0, 0, 0, 0),
active_handle: unsafe { MonitorFromWindow(GetFocus(), MONITOR_DEFAULTTONEAREST) },
friendly_names: gdi_display_name_to_friendly_monitor_names()?,
gdi_to_adapater: gdi_display_name_to_adapter_names(),
};
unsafe {
EnumDisplayMonitors(
std::ptr::null_mut(),
std::ptr::null(),
Some(callback),
&mut info as *mut _ as LPARAM,
);
}
let mut info = ScreenInfoHelper::new()?;
info.enumerate();
let main = info
.primary
@ -275,6 +169,129 @@ impl Connection {
}
}
pub(crate) struct ScreenInfoHelper {
primary: Option<ScreenInfo>,
active: Option<ScreenInfo>,
by_name: HashMap<String, ScreenInfo>,
virtual_rect: ScreenRect,
active_handle: HMONITOR,
friendly_names: HashMap<String, String>,
gdi_to_adapater: HashMap<String, String>,
}
impl ScreenInfoHelper {
pub fn new() -> anyhow::Result<Self> {
Ok(Self {
primary: None,
active: None,
by_name: HashMap::new(),
virtual_rect: euclid::rect(0, 0, 0, 0),
active_handle: unsafe { MonitorFromWindow(GetFocus(), MONITOR_DEFAULTTONEAREST) },
friendly_names: gdi_display_name_to_friendly_monitor_names()?,
gdi_to_adapater: gdi_display_name_to_adapter_names(),
})
}
pub fn enumerate(&mut self) {
unsafe extern "system" fn callback(
mon: HMONITOR,
_hdc: HDC,
_rect: *mut RECT,
data: LPARAM,
) -> i32 {
let info: &mut ScreenInfoHelper = &mut *(data as *mut ScreenInfoHelper);
let mut mi: MONITORINFOEXW = std::mem::zeroed();
mi.cbSize = std::mem::size_of::<MONITORINFOEXW>() as u32;
GetMonitorInfoW(mon, &mut mi as *mut MONITORINFOEXW as *mut MONITORINFO);
let mut devmode: DEVMODEW = std::mem::zeroed();
devmode.dmSize = std::mem::size_of::<DEVMODEW>() as u16;
let max_fps =
if EnumDisplaySettingsW(mi.szDevice.as_ptr(), ENUM_CURRENT_SETTINGS, &mut devmode)
!= 0
&& (devmode.dmFields & DM_DISPLAYFREQUENCY) != 0
&& devmode.dmDisplayFrequency > 1
{
Some(devmode.dmDisplayFrequency as usize)
} else {
None
};
let monitor_name = info.monitor_name(&mi);
let screen_info = ScreenInfo {
name: monitor_name.clone(),
rect: euclid::rect(
mi.rcMonitor.left as isize,
mi.rcMonitor.top as isize,
mi.rcMonitor.right as isize - mi.rcMonitor.left as isize,
mi.rcMonitor.bottom as isize - mi.rcMonitor.top as isize,
),
scale: 1.0,
max_fps,
};
info.virtual_rect = info.virtual_rect.union(&screen_info.rect);
if mi.dwFlags & MONITORINFOF_PRIMARY == MONITORINFOF_PRIMARY {
info.primary.replace(screen_info.clone());
}
if mon == info.active_handle {
info.active.replace(screen_info.clone());
}
info.by_name.insert(monitor_name, screen_info);
winapi::shared::ntdef::TRUE.into()
}
unsafe {
EnumDisplayMonitors(
std::ptr::null_mut(),
std::ptr::null(),
Some(callback),
self as *mut _ as LPARAM,
);
}
}
pub fn monitor_name(&self, mi: &MONITORINFOEXW) -> String {
unsafe {
let monitor_name = wstr(&mi.szDevice);
let friendly_name = match self.friendly_names.get(&monitor_name) {
Some(name) => name.to_string(),
None => {
// Fall back to EnumDisplayDevicesW.
// It likely has a terribly generic name like "Generic PnP Monitor".
let mut display_device: DISPLAY_DEVICEW = std::mem::zeroed();
display_device.cb = std::mem::size_of::<DISPLAY_DEVICEW>() as u32;
if EnumDisplayDevicesW(mi.szDevice.as_ptr(), 0, &mut display_device, 0) != 0 {
wstr(&display_device.DeviceString)
} else {
"Unknown".to_string()
}
}
};
let adapter_name = match self.gdi_to_adapater.get(&monitor_name) {
Some(name) => name.to_string(),
None => "Unknown".to_string(),
};
// "\\.\DISPLAY1" -> "DISPLAY1"
let monitor_name = if let Some(name) = monitor_name.strip_prefix("\\\\.\\") {
name.to_string()
} else {
monitor_name
};
let monitor_name = format!("{monitor_name}: {friendly_name} on {adapter_name}");
monitor_name
}
}
}
/// Convert a UCS2 wide char string to a Rust String
fn wstr(slice: &[u16]) -> String {
let len = slice.iter().position(|&c| c == 0).unwrap_or(0);

View File

@ -251,6 +251,30 @@ impl WindowInner {
Ok(gl_state)
}
fn get_effective_dpi(&self) -> usize {
let actual_dpi = unsafe { GetDpiForWindow(self.hwnd.0) } as f64;
if self.config.dpi_by_screen.is_empty() {
return self.config.dpi.unwrap_or(actual_dpi) as usize;
}
unsafe {
let mut mi: MONITORINFOEXW = std::mem::zeroed();
mi.cbSize = std::mem::size_of::<MONITORINFOEXW>() as u32;
let mon = MonitorFromWindow(self.hwnd.0, MONITOR_DEFAULTTONEAREST);
GetMonitorInfoW(mon, &mut mi as *mut MONITORINFOEXW as *mut MONITORINFO);
if let Ok(info) = crate::os::windows::connection::ScreenInfoHelper::new() {
let name = info.monitor_name(&mi);
if let Some(dpi) = self.config.dpi_by_screen.get(&name).copied() {
return dpi as usize;
}
}
actual_dpi as usize
}
}
/// Check if we need to generate a resize callback.
/// Calls resize if needed.
/// Returns true if we did.
@ -282,7 +306,7 @@ impl WindowInner {
let current_dims = Dimensions {
pixel_width,
pixel_height,
dpi: unsafe { GetDpiForWindow(self.hwnd.0) as usize },
dpi: self.get_effective_dpi(),
};
let same = self
@ -1089,7 +1113,7 @@ unsafe fn wm_nccalcsize(hwnd: HWND, _msg: UINT, wparam: WPARAM, lparam: LPARAM)
}
if inner.saved_placement.is_none() {
let dpi = GetDpiForWindow(hwnd);
let dpi = inner.get_effective_dpi() as u32;
let frame_x = GetSystemMetricsForDpi(SM_CXFRAME, dpi);
let frame_y = GetSystemMetricsForDpi(SM_CYFRAME, dpi);
let padding = GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi);
@ -1160,7 +1184,7 @@ unsafe fn wm_nchittest(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) ->
// The adjustment in NCCALCSIZE messes with the detection
// of the top hit area so manually fixing that.
let dpi = GetDpiForWindow(hwnd);
let dpi = inner.get_effective_dpi() as u32;
let frame_x = GetSystemMetricsForDpi(SM_CXFRAME, dpi) as isize;
let frame_y = GetSystemMetricsForDpi(SM_CYFRAME, dpi) as isize;
let padding = GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) as isize;