From bb1a6c23c5e0be6013a87a85460aff6efa9ea0d0 Mon Sep 17 00:00:00 2001 From: Delan Azabani Date: Thu, 14 Sep 2023 21:24:38 +0800 Subject: [PATCH] minibrowser: implement HiDPI support (#30343) --- ports/servoshell/app.rs | 73 ++++++++++++++----- ports/servoshell/headed_window.rs | 54 +++++++------- ports/servoshell/headless_window.rs | 25 ++++--- ports/servoshell/minibrowser.rs | 22 ++++-- .../platform/windows/servo.exe.manifest | 6 +- ports/servoshell/window_trait.rs | 17 ++++- 6 files changed, 130 insertions(+), 67 deletions(-) diff --git a/ports/servoshell/app.rs b/ports/servoshell/app.rs index 74565d1d1e3..2f44453dc46 100644 --- a/ports/servoshell/app.rs +++ b/ports/servoshell/app.rs @@ -10,13 +10,14 @@ use std::rc::Rc; use std::time::Instant; use gleam::gl; -use log::{trace, warn}; +use log::{info, trace, warn}; use servo::compositing::windowing::EmbedderEvent; use servo::config::opts; use servo::servo_config::pref; use servo::Servo; use surfman::GLApi; use webxr::glwindow::GlWindowDiscovery; +use winit::event::WindowEvent; use winit::event_loop::EventLoopWindowTarget; use winit::window::WindowId; @@ -95,9 +96,8 @@ impl App { webrender_surfman.make_gl_context_current().unwrap(); debug_assert_eq!(webrender_gl.get_error(), gleam::gl::NO_ERROR); - // Set up egui context for minibrowser ui - // Adapted from https://github.com/emilk/egui/blob/9478e50d012c5138551c38cbee16b07bc1fcf283/crates/egui_glow/examples/pure_glow.rs - app.minibrowser = Some(Minibrowser::new(&webrender_surfman, &events_loop).into()); + app.minibrowser = + Some(Minibrowser::new(&webrender_surfman, &events_loop, window.as_ref()).into()); } if let Some(mut minibrowser) = app.minibrowser() { @@ -119,15 +119,23 @@ impl App { let t_start = Instant::now(); let mut t = t_start; let ev_waker = events_loop.create_event_loop_waker(); - events_loop.run_forever(move |e, w, control_flow| { + events_loop.run_forever(move |event, w, control_flow| { let now = Instant::now(); - match e { - // Uncomment to filter out logging of DeviceEvent, which can be very noisy. + match event { + // Uncomment to filter out logging of common events, which can be very noisy. // winit::event::Event::DeviceEvent { .. } => {}, - _ => trace!("@{:?} (+{:?}) {:?}", now - t_start, now - t, e), + // winit::event::Event::WindowEvent { + // event: WindowEvent::CursorMoved { .. }, + // .. + // } => {}, + // winit::event::Event::MainEventsCleared => {}, + // winit::event::Event::RedrawEventsCleared => {}, + // winit::event::Event::UserEvent(..) => {}, + // winit::event::Event::NewEvents(..) => {}, + _ => trace!("@{:?} (+{:?}) {:?}", now - t_start, now - t, event), } t = now; - match e { + match event { winit::event::Event::NewEvents(winit::event::StartCause::Init) => { let surfman = window.webrender_surfman(); @@ -182,7 +190,7 @@ impl App { return; } - if let winit::event::Event::RedrawRequested(_) = e { + if let winit::event::Event::RedrawRequested(_) = event { // We need to redraw the window for some reason. trace!("RedrawRequested"); @@ -207,20 +215,45 @@ impl App { // Handle the event let mut consumed = false; if let Some(mut minibrowser) = app.minibrowser() { - if let winit::event::Event::WindowEvent { ref event, .. } = e { - let response = minibrowser.context.on_event(&event); - if response.repaint { - // Request a winit redraw event, so we can recomposite, update and paint the - // minibrowser, and present the new frame. - window.winit_window().unwrap().request_redraw(); - } + match event { + winit::event::Event::WindowEvent { + event: WindowEvent::ScaleFactorChanged { scale_factor, .. }, + .. + } => { + // Intercept any ScaleFactorChanged events away from EguiGlow::on_event, so + // we can use our own logic for calculating the scale factor and set egui’s + // scale factor to that value manually. + let effective_scale_factor = window.hidpi_factor().get(); + info!( + "window scale factor changed to {}, setting scale factor to {}", + scale_factor, effective_scale_factor + ); + minibrowser + .context + .egui_ctx + .set_pixels_per_point(effective_scale_factor); - // TODO how do we handle the tab key? (see doc for consumed) - consumed = response.consumed; + // Request a winit redraw event, so we can recomposite, update and paint + // the minibrowser, and present the new frame. + window.winit_window().unwrap().request_redraw(); + }, + winit::event::Event::WindowEvent { ref event, .. } => { + let response = minibrowser.context.on_event(&event); + if response.repaint { + // Request a winit redraw event, so we can recomposite, update and paint + // the minibrowser, and present the new frame. + window.winit_window().unwrap().request_redraw(); + } + + // TODO how do we handle the tab key? (see doc for consumed) + // Note that servo doesn’t yet support tabbing through links and inputs + consumed = response.consumed; + }, + _ => {}, } } if !consumed { - app.queue_embedder_events_for_winit_event(e); + app.queue_embedder_events_for_winit_event(event); } let animating = app.is_animating(); diff --git a/ports/servoshell/headed_window.rs b/ports/servoshell/headed_window.rs index 9e35140a4e9..dd801fb343a 100644 --- a/ports/servoshell/headed_window.rs +++ b/ports/servoshell/headed_window.rs @@ -8,7 +8,8 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::rc::Rc; -use euclid::{Angle, Point2D, Rotation3D, Scale, Size2D, UnknownUnit, Vector2D, Vector3D}; +use euclid::num::Zero; +use euclid::{Angle, Length, Point2D, Rotation3D, Scale, Size2D, UnknownUnit, Vector2D, Vector3D}; use log::{debug, info, trace}; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; use servo::compositing::windowing::{ @@ -48,7 +49,7 @@ pub struct Window { webrender_surfman: WebrenderSurfman, screen_size: Size2D, inner_size: Cell>, - toolbar_height: Cell, + toolbar_height: Cell>, mouse_down_button: Cell>, mouse_down_point: Cell>, primary_monitor: winit::monitor::MonitorHandle, @@ -155,7 +156,7 @@ impl Window { device_pixel_ratio_override, xr_window_poses: RefCell::new(vec![]), modifiers_state: Cell::new(ModifiersState::empty()), - toolbar_height: Cell::new(0.0), + toolbar_height: Cell::new(Default::default()), } } @@ -241,7 +242,7 @@ impl Window { ) { use servo::script_traits::MouseButton; - let max_pixel_dist = 10.0 * self.servo_hidpi_factor().get(); + let max_pixel_dist = 10.0 * self.hidpi_factor().get(); let mouse_button = match &button { winit::event::MouseButton::Left => MouseButton::Left, winit::event::MouseButton::Right => MouseButton::Right, @@ -280,20 +281,6 @@ impl Window { .borrow_mut() .push(EmbedderEvent::MouseWindowEventClass(event)); } - - fn device_hidpi_factor(&self) -> Scale { - Scale::new(self.winit_window.scale_factor() as f32) - } - - fn servo_hidpi_factor(&self) -> Scale { - match self.device_pixel_ratio_override { - Some(override_value) => Scale::new(override_value), - _ => match opts::get().output_file { - Some(_) => Scale::new(1.0), - None => self.device_hidpi_factor(), - }, - } - } } impl WindowPortsMethods for Window { @@ -305,8 +292,18 @@ impl WindowPortsMethods for Window { !self.event_queue.borrow().is_empty() } + fn device_hidpi_factor(&self) -> Scale { + Scale::new(self.winit_window.scale_factor() as f32) + } + + fn device_pixel_ratio_override( + &self, + ) -> Option> { + self.device_pixel_ratio_override.map(Scale::new) + } + fn page_height(&self) -> f32 { - let dpr = self.servo_hidpi_factor(); + let dpr = self.hidpi_factor(); let size = self.winit_window.inner_size(); size.height as f32 * dpr.get() } @@ -406,14 +403,13 @@ impl WindowPortsMethods for Window { } }, winit::event::WindowEvent::CursorMoved { position, .. } => { - let (x, y): (f64, f64) = position.into(); - let y = y - f64::from(self.toolbar_height.get()); - self.mouse_pos.set(Point2D::new(x, y).to_i32()); + let toolbar_height = self.toolbar_height.get() * self.hidpi_factor(); + let mut position = winit_position_to_euclid_point(position).to_f32(); + position -= Size2D::from_lengths(Length::zero(), toolbar_height); + self.mouse_pos.set(position.to_i32()); self.event_queue .borrow_mut() - .push(EmbedderEvent::MouseWindowMoveEventClass(Point2D::new( - x as f32, y as f32, - ))); + .push(EmbedderEvent::MouseWindowMoveEventClass(position.to_f32())); }, winit::event::WindowEvent::MouseWheel { delta, phase, .. } => { let (mut dx, mut dy, mode) = match delta { @@ -512,7 +508,7 @@ impl WindowPortsMethods for Window { Some(&self.winit_window) } - fn set_toolbar_height(&self, height: f32) { + fn set_toolbar_height(&self, height: Length) { self.toolbar_height.set(height); } } @@ -533,8 +529,8 @@ impl WindowMethods for Window { let inner_size = winit_size_to_euclid_size(self.winit_window.inner_size()).to_f32(); // Subtract the minibrowser toolbar height if any - let toolbar_height = self.toolbar_height.get(); - let viewport_size = inner_size - Size2D::new(0f32, toolbar_height); + let toolbar_height = self.toolbar_height.get() * self.hidpi_factor(); + let viewport_size = inner_size - Size2D::from_lengths(Length::zero(), toolbar_height); let viewport_origin = DeviceIntPoint::zero(); // bottom left let viewport = DeviceIntRect::new(viewport_origin, viewport_size.to_i32()); @@ -547,7 +543,7 @@ impl WindowMethods for Window { screen, // FIXME: Winit doesn't have API for available size. Fallback to screen size screen_avail: screen, - hidpi_factor: self.servo_hidpi_factor(), + hidpi_factor: self.hidpi_factor(), } } diff --git a/ports/servoshell/headless_window.rs b/ports/servoshell/headless_window.rs index 5da2b2e5cb9..751dbf87da0 100644 --- a/ports/servoshell/headless_window.rs +++ b/ports/servoshell/headless_window.rs @@ -7,7 +7,7 @@ use std::cell::Cell; use std::rc::Rc; -use euclid::{Point2D, Rotation3D, Scale, Size2D, UnknownUnit, Vector3D}; +use euclid::{Length, Point2D, Rotation3D, Scale, Size2D, UnknownUnit, Vector3D}; use servo::compositing::windowing::{ AnimationState, EmbedderCoordinates, EmbedderEvent, WindowMethods, }; @@ -52,13 +52,6 @@ impl Window { Rc::new(window) } - - fn servo_hidpi_factor(&self) -> Scale { - match self.device_pixel_ratio_override { - Some(override_value) => Scale::new(override_value), - _ => Scale::new(1.0), - } - } } impl WindowPortsMethods for Window { @@ -74,6 +67,16 @@ impl WindowPortsMethods for Window { unsafe { winit::window::WindowId::dummy() } } + fn device_hidpi_factor(&self) -> Scale { + Scale::new(1.0) + } + + fn device_pixel_ratio_override( + &self, + ) -> Option> { + self.device_pixel_ratio_override.map(Scale::new) + } + fn page_height(&self) -> f32 { let height = self .webrender_surfman @@ -81,7 +84,7 @@ impl WindowPortsMethods for Window { .unwrap_or(None) .map(|info| info.size.height) .unwrap_or(0); - let dpr = self.servo_hidpi_factor(); + let dpr = self.hidpi_factor(); height as f32 * dpr.get() } @@ -112,14 +115,14 @@ impl WindowPortsMethods for Window { None } - fn set_toolbar_height(&self, _height: f32) { + fn set_toolbar_height(&self, _height: Length) { unimplemented!("headless Window only") } } impl WindowMethods for Window { fn get_coordinates(&self) -> EmbedderCoordinates { - let dpr = self.servo_hidpi_factor(); + let dpr = self.hidpi_factor(); let size = self .webrender_surfman .context_surface_info() diff --git a/ports/servoshell/minibrowser.rs b/ports/servoshell/minibrowser.rs index 0bf22d77587..75adfbe79d5 100644 --- a/ports/servoshell/minibrowser.rs +++ b/ports/servoshell/minibrowser.rs @@ -7,8 +7,10 @@ use std::sync::Arc; use std::time::Instant; use egui::{Key, Modifiers, TopBottomPanel}; +use euclid::Length; use log::{trace, warn}; use servo::compositing::windowing::EmbedderEvent; +use servo::servo_geometry::DeviceIndependentPixel; use servo::servo_url::ServoUrl; use servo::webrender_surfman::WebrenderSurfman; @@ -20,7 +22,7 @@ use crate::window_trait::WindowPortsMethods; pub struct Minibrowser { pub context: EguiGlow, pub event_queue: RefCell>, - pub toolbar_height: Cell, + pub toolbar_height: Cell>, last_update: Instant, location: RefCell, @@ -34,15 +36,25 @@ pub enum MinibrowserEvent { } impl Minibrowser { - pub fn new(webrender_surfman: &WebrenderSurfman, events_loop: &EventsLoop) -> Self { + pub fn new( + webrender_surfman: &WebrenderSurfman, + events_loop: &EventsLoop, + window: &dyn WindowPortsMethods, + ) -> Self { let gl = unsafe { glow::Context::from_loader_function(|s| webrender_surfman.get_proc_address(s)) }; + // Adapted from https://github.com/emilk/egui/blob/9478e50d012c5138551c38cbee16b07bc1fcf283/crates/egui_glow/examples/pure_glow.rs + let context = EguiGlow::new(events_loop.as_winit(), Arc::new(gl), None); + context + .egui_ctx + .set_pixels_per_point(window.hidpi_factor().get()); + Self { - context: EguiGlow::new(events_loop.as_winit(), Arc::new(gl), None), + context, event_queue: RefCell::new(vec![]), - toolbar_height: 0f32.into(), + toolbar_height: Default::default(), last_update: Instant::now(), location: RefCell::new(String::default()), location_dirty: false.into(), @@ -96,7 +108,7 @@ impl Minibrowser { ); }); - toolbar_height.set(ctx.used_rect().height()); + toolbar_height.set(Length::new(ctx.used_rect().height())); *last_update = now; }); } diff --git a/ports/servoshell/platform/windows/servo.exe.manifest b/ports/servoshell/platform/windows/servo.exe.manifest index 23b2abe1b72..0238802919e 100644 --- a/ports/servoshell/platform/windows/servo.exe.manifest +++ b/ports/servoshell/platform/windows/servo.exe.manifest @@ -17,7 +17,11 @@ - true + + + + true/pm + PerMonitorV2 diff --git a/ports/servoshell/window_trait.rs b/ports/servoshell/window_trait.rs index 60f988291d0..2babc09883d 100644 --- a/ports/servoshell/window_trait.rs +++ b/ports/servoshell/window_trait.rs @@ -5,8 +5,12 @@ //! Definition of Window. //! Implemented by headless and headed windows. +use euclid::{Length, Scale}; use servo::compositing::windowing::{EmbedderEvent, WindowMethods}; +use servo::config::opts; use servo::embedder_traits::Cursor; +use servo::servo_geometry::DeviceIndependentPixel; +use servo::style_traits::DevicePixel; use servo::webrender_api::units::{DeviceIntPoint, DeviceIntSize}; use crate::events_loop::WakerEvent; @@ -18,6 +22,17 @@ pub trait WindowPortsMethods: WindowMethods { fn get_events(&self) -> Vec; fn id(&self) -> winit::window::WindowId; fn has_events(&self) -> bool; + fn hidpi_factor(&self) -> Scale { + self.device_pixel_ratio_override() + .unwrap_or_else(|| match opts::get().output_file { + Some(_) => Scale::new(1.0), + None => self.device_hidpi_factor(), + }) + } + fn device_hidpi_factor(&self) -> Scale; + fn device_pixel_ratio_override( + &self, + ) -> Option>; fn page_height(&self) -> f32; fn get_fullscreen(&self) -> bool; fn queue_embedder_events_for_winit_event(&self, event: winit::event::WindowEvent<'_>); @@ -32,5 +47,5 @@ pub trait WindowPortsMethods: WindowMethods { events_loop: &winit::event_loop::EventLoopWindowTarget, ) -> Box; fn winit_window(&self) -> Option<&winit::window::Window>; - fn set_toolbar_height(&self, height: f32); + fn set_toolbar_height(&self, height: Length); }