On first resize interaction, or a first window refocus after initial window display whichever comes first, the window size jumps to double its original size, on a dual monitor desktop where one monitor is set to 200% scaling and the other to 100%.
This is consistent on every run of the application.
Code for a bare window reproducing the issue is attached at the very bottom below.
use eframe::egui;
const INITIAL_INNER_SIZE: [f32; 2] = [800.0, 300.0];
const MIN_INNER_SIZE: [f32; 2] = [400.0, 150.0];
fn main() -> eframe::Result {
println!(
"[repro] requested viewport: decorations=false resizable=true initial_inner_size={} min_inner_size={} eframe=0.35.0",
format_size(INITIAL_INNER_SIZE),
format_size(MIN_INNER_SIZE),
);
println!(
"[repro] session: XDG_SESSION_TYPE={:?} XDG_CURRENT_DESKTOP={:?} WAYLAND_DISPLAY={:?} DISPLAY={:?}",
std::env::var("XDG_SESSION_TYPE").ok(),
std::env::var("XDG_CURRENT_DESKTOP").ok(),
std::env::var("WAYLAND_DISPLAY").ok(),
std::env::var("DISPLAY").ok(),
);
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_title("eframe window size jump repro")
.with_decorations(false)
.with_resizable(true)
.with_inner_size(INITIAL_INNER_SIZE)
.with_min_inner_size(MIN_INNER_SIZE),
..Default::default()
};
eframe::run_native(
"eframe window size jump repro",
options,
Box::new(|_cc| Ok(Box::<ReproApp>::default())),
)
}
#[derive(Default)]
struct ReproApp {
last_geometry: Option<Geometry>,
}
impl eframe::App for ReproApp {
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
self.log_geometry_if_changed(ui.ctx());
egui::CentralPanel::default().show(ui, |ui| {
ui.vertical_centered(|ui| {
ui.add_space(90.0);
ui.label("Switch focus to another window and back");
ui.label("Watch stdout for a 2x size jump");
});
});
}
}
impl ReproApp {
fn log_geometry_if_changed(&mut self, ctx: &egui::Context) {
let current = Geometry::from_context(ctx);
match self.last_geometry.as_ref() {
None => {
println!("[repro] first-ui-pass: {}", current.describe());
}
Some(previous) if previous != ¤t => {
println!(
"[repro] viewport-geometry-changed: resized={} content_size={} viewport_size={} pixels_per_point={} native_pixels_per_point={} zoom_factor={} monitor_size_points={} focused={:?}->{:?}",
previous.content_size != current.content_size
|| previous.viewport_size != current.viewport_size,
format_vec2_transition(previous.content_size, current.content_size),
format_vec2_transition(previous.viewport_size, current.viewport_size),
format_f32_transition(previous.pixels_per_point, current.pixels_per_point),
format_optional_f32_transition(
previous.native_pixels_per_point,
current.native_pixels_per_point,
),
format_f32_transition(previous.zoom_factor, current.zoom_factor),
format_optional_vec2_transition(previous.monitor_size, current.monitor_size),
previous.focused,
current.focused,
);
}
Some(_) => return,
}
self.last_geometry = Some(current);
}
}
#[derive(Debug, Clone, PartialEq)]
struct Geometry {
content_size: egui::Vec2,
viewport_size: egui::Vec2,
pixels_per_point: f32,
zoom_factor: f32,
native_pixels_per_point: Option<f32>,
monitor_size: Option<egui::Vec2>,
focused: Option<bool>,
}
impl Geometry {
fn from_context(ctx: &egui::Context) -> Self {
let (viewport, content_size, viewport_size) = ctx.input(|input| {
(
input.viewport().clone(),
input.content_rect().size(),
input.viewport_rect().size(),
)
});
Self {
content_size,
viewport_size,
pixels_per_point: ctx.pixels_per_point(),
zoom_factor: ctx.zoom_factor(),
native_pixels_per_point: viewport.native_pixels_per_point,
monitor_size: viewport.monitor_size,
focused: viewport.focused,
}
}
fn describe(&self) -> String {
format!(
"content_size={} viewport_size={} pixels_per_point={:.3} native_pixels_per_point={} zoom_factor={:.3} monitor_size_points={} focused={:?}",
format_vec2(self.content_size),
format_vec2(self.viewport_size),
self.pixels_per_point,
format_optional_f32(self.native_pixels_per_point),
self.zoom_factor,
format_optional_vec2(self.monitor_size),
self.focused,
)
}
}
fn format_size(value: [f32; 2]) -> String {
format!("{:.1}x{:.1}", value[0], value[1])
}
fn format_optional_f32(value: Option<f32>) -> String {
value.map_or_else(|| "unknown".to_string(), |value| format!("{value:.3}"))
}
fn format_f32_transition(previous: f32, current: f32) -> String {
format!("{previous:.3}->{current:.3}")
}
fn format_optional_f32_transition(previous: Option<f32>, current: Option<f32>) -> String {
format!(
"{}->{}",
format_optional_f32(previous),
format_optional_f32(current)
)
}
fn format_optional_vec2(value: Option<egui::Vec2>) -> String {
value.map_or_else(|| "unknown".to_string(), format_vec2)
}
fn format_vec2_transition(previous: egui::Vec2, current: egui::Vec2) -> String {
format!(
"{}->{} ratio={}",
format_vec2(previous),
format_vec2(current),
format_vec2_ratio(previous, current)
)
}
fn format_optional_vec2_transition(
previous: Option<egui::Vec2>,
current: Option<egui::Vec2>,
) -> String {
match (previous, current) {
(Some(previous), Some(current)) => format_vec2_transition(previous, current),
_ => format!(
"{}->{}",
format_optional_vec2(previous),
format_optional_vec2(current)
),
}
}
fn format_vec2_ratio(previous: egui::Vec2, current: egui::Vec2) -> String {
format!(
"{:.3}x{:.3}",
ratio_component(previous.x, current.x),
ratio_component(previous.y, current.y)
)
}
fn ratio_component(previous: f32, current: f32) -> f32 {
if previous.abs() <= f32::EPSILON {
f32::NAN
} else {
current / previous
}
}
fn format_vec2(value: egui::Vec2) -> String {
format!("{:.1}x{:.1}", value.x, value.y)
}
On first resize interaction, or a first window refocus after initial window display whichever comes first, the window size jumps to double its original size, on a dual monitor desktop where one monitor is set to 200% scaling and the other to 100%.
This is consistent on every run of the application.
The window is created with:
Code for a bare window reproducing the issue is attached at the very bottom below.
Application/window setup:
Observed behavior:
On first custom edge resize interaction, before sending the resize command, the app logged:
Immediately after the resize command, the next viewport geometry event reported:
It's consistently only the first window resize or refocus which trigger the size jump; subsequent resize events behave as expected.
Issue Summary
Environment:
minimally reproducing code:
src/main.rs
Cargo.toml
Related issues:
#7095
rust-windowing/winit#2921
rust-windowing/winit#3485