Multiply input coords by window scale_factor so Retina clicks land

GPUI delivers logical (CSS) pixels but Servo expects device pixels.
On a 2x Retina display the page rendered at half resolution and every
click landed in roughly the upper-left quadrant of the page — the
second independent root cause that surfaces immediately after T1's
show()/focus() lets input reach Servo at all.

Single conversion boundary inside web_surface_geometry.rs:
viewport_dimension, scroll_dimension, and click_coordinate each
multiply once by window.scale_factor() before truncating to integer
device pixels. positive_scale_or_one() guards against a zero/negative/
NaN scale_factor reaching the arithmetic — falling back to 1.0 keeps
coordinates valid even if the platform reports nonsense (system
boundary validation per CLAUDE.md, not internal trust).

scale_factor is plumbed through controller and view layer, sourced
from window.scale_factor() at every record_* boundary so all four
inputs (click, scroll, hover, viewport) stay in sync. Existing tests
pin the 1.0 path; new retina_scale_factor_doubles_every_input_coordinate
locks the 2.0 path against a future regression.

cargo test ely_app --bin ely_app: 104 passed (was 103 + new test).
This commit is contained in:
2026-05-10 01:53:51 -04:00
parent 22ba8517d1
commit c20daf36b9
6 changed files with 136 additions and 39 deletions
+15 -5
View File
@@ -102,8 +102,9 @@ impl WebSurfaceStore {
tab_id: &TabId,
requested_url: &str,
delta: Point<Pixels>,
scale_factor: f32,
) -> bool {
let Some(delta) = WebSurfaceScrollDelta::from_point(delta) else {
let Some(delta) = WebSurfaceScrollDelta::from_point(delta, scale_factor) else {
return false;
};
@@ -130,8 +131,13 @@ impl WebSurfaceStore {
true
}
pub(super) fn record_viewport_size(&mut self, tab_id: &TabId, bounds: Bounds<Pixels>) -> bool {
let Some(size) = WebSurfaceSize::from_bounds(bounds) else {
pub(super) fn record_viewport_size(
&mut self,
tab_id: &TabId,
bounds: Bounds<Pixels>,
scale_factor: f32,
) -> bool {
let Some(size) = WebSurfaceSize::from_bounds(bounds, scale_factor) else {
return false;
};
let surface = self.surface_mut(tab_id);
@@ -162,13 +168,15 @@ impl WebSurfaceStore {
&mut self,
tab_id: &TabId,
position: Point<Pixels>,
scale_factor: f32,
) -> bool {
let surface = self.surfaces.get_mut(tab_id).filter(|surface| surface.viewport_bounds.is_some());
let Some(surface) = surface else {
return false;
};
let bounds = surface.viewport_bounds.expect("viewport_bounds checked above");
let Some(point) = WebSurfaceClickPoint::from_window_position(bounds, position) else {
let Some(point) = WebSurfaceClickPoint::from_window_position(bounds, position, scale_factor)
else {
return false;
};
surface.hover_point = Some(point);
@@ -180,13 +188,15 @@ impl WebSurfaceStore {
tab_id: &TabId,
requested_url: &str,
position: Point<Pixels>,
scale_factor: f32,
) -> bool {
let Some(bounds) =
self.surfaces.get(tab_id).and_then(|surface| surface.viewport_bounds)
else {
return false;
};
let Some(point) = WebSurfaceClickPoint::from_window_position(bounds, position) else {
let Some(point) = WebSurfaceClickPoint::from_window_position(bounds, position, scale_factor)
else {
return false;
};