From 67ff35687face9f1580398b8755f9758d386d03f Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Fri, 6 Jun 2014 16:04:34 -0400 Subject: [PATCH] servo: Merge #2603 - Use some typed units in compositor and windowing (from mbrubeck:units); r=pcwalton This is a rebased, squashed, and updated version of #2444. Source-Repo: https://github.com/servo/servo Source-Revision: 6c382243c4b3de9f0eec9cd71c757897ffd1b2e0 --- .../components/main/compositing/compositor.rs | 144 ++++++++++-------- .../main/compositing/compositor_layer.rs | 82 +++++----- .../main/platform/common/glfw_windowing.rs | 26 ++-- .../main/platform/common/glut_windowing.rs | 34 +++-- servo/src/components/main/windowing.rs | 20 +-- servo/src/components/util/geometry.rs | 32 ++++ servo/src/components/util/opts.rs | 7 +- 7 files changed, 213 insertions(+), 132 deletions(-) diff --git a/servo/src/components/main/compositing/compositor.rs b/servo/src/components/main/compositing/compositor.rs index dd9aebbea867..006653027c38 100644 --- a/servo/src/components/main/compositing/compositor.rs +++ b/servo/src/components/main/compositing/compositor.rs @@ -16,9 +16,10 @@ use windowing::{WindowEvent, WindowMethods, WindowNavigateMsg, ZoomWindowEvent}; use azure::azure_hl::{SourceSurfaceMethods, Color}; use azure::azure_hl; use geom::matrix::identity; -use geom::point::Point2D; +use geom::point::{Point2D, TypedPoint2D}; use geom::rect::Rect; -use geom::size::Size2D; +use geom::size::{Size2D, TypedSize2D}; +use geom::scale_factor::ScaleFactor; use layers::layers::{ContainerLayer, ContainerLayerKind}; use layers::platform::surface::NativeCompositingGraphicsContext; use layers::rendergl; @@ -31,6 +32,7 @@ use servo_msg::compositor_msg::{LayerId, ReadyState, RenderState, ScrollPolicy, use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, LoadUrlMsg, NavigateMsg}; use servo_msg::constellation_msg::{PipelineId, ResizedWindowMsg}; use servo_msg::constellation_msg; +use servo_util::geometry::{DevicePixel, PagePx, ScreenPx}; use servo_util::opts::Opts; use servo_util::time::{profile, ProfilerChan}; use servo_util::{time, url}; @@ -60,7 +62,10 @@ pub struct IOCompositor { scene: Scene, /// The application window size. - window_size: Size2D, + window_size: TypedSize2D, + + /// The device pixel ratio for this window. + hidpi_factor: ScaleFactor, /// The platform-specific graphics context. graphics_context: NativeCompositingGraphicsContext, @@ -78,7 +83,7 @@ pub struct IOCompositor { recomposite: bool, /// Keeps track of the current zoom factor. - world_zoom: f32, + world_zoom: ScaleFactor, /// Tracks whether the zoom action has happend recently. zoom_action: bool, @@ -124,16 +129,7 @@ impl IOCompositor { // list. This is only here because we don't have that logic in the renderer yet. let root_layer = Rc::new(ContainerLayer()); let window_size = window.size(); - - let hidpi_factor = match opts.device_pixels_per_px { - Some(dppx) => dppx, - None => match opts.output_file { - Some(_) => 1.0, - None => window.hidpi_factor(), - } - }; - - root_layer.common.borrow_mut().set_transform(identity().scale(hidpi_factor, hidpi_factor, 1f32)); + let hidpi_factor = window.hidpi_factor(); IOCompositor { window: window, @@ -142,14 +138,15 @@ impl IOCompositor { context: rendergl::init_render_context(), root_layer: root_layer.clone(), root_pipeline: None, - scene: Scene(ContainerLayerKind(root_layer), window_size, identity()), - window_size: Size2D(window_size.width as uint, window_size.height as uint), + scene: Scene(ContainerLayerKind(root_layer), window_size.to_untyped(), identity()), + window_size: window_size.as_uint(), + hidpi_factor: hidpi_factor, graphics_context: CompositorTask::create_graphics_context(), composite_ready: false, shutting_down: false, done: false, recomposite: false, - world_zoom: hidpi_factor, + world_zoom: ScaleFactor(1.0), zoom_action: false, zoom_time: 0f64, ready_state: Blank, @@ -171,6 +168,7 @@ impl IOCompositor { port, constellation_chan, profiler_chan); + compositor.update_zoom_transform(); // Starts the compositor, which listens for messages on the specified port. compositor.run(); @@ -180,7 +178,7 @@ impl IOCompositor { // Tell the constellation about the initial window size. { let ConstellationChan(ref chan) = self.constellation_chan; - chan.send(ResizedWindowMsg(self.window_size)); + chan.send(ResizedWindowMsg(self.window_size.to_untyped())); } // Enter the main event loop. @@ -341,12 +339,10 @@ impl IOCompositor { self.root_pipeline = Some(frame_tree.pipeline.clone()); // Initialize the new constellation channel by sending it the root window size. - let window_size = self.window.size(); - let window_size = Size2D(window_size.width as uint, - window_size.height as uint); + let window_size = self.window.size().as_uint(); { let ConstellationChan(ref chan) = new_constellation_chan; - chan.send(ResizedWindowMsg(window_size)); + chan.send(ResizedWindowMsg(window_size.to_untyped())); } self.constellation_chan = new_constellation_chan; @@ -424,17 +420,19 @@ impl IOCompositor { self.ask_for_tiles(); } + /// The size of the content area in CSS px at the current zoom level + fn page_window(&self) -> TypedSize2D { + self.window_size.as_f32() / self.device_pixels_per_page_px() + } + fn set_layer_page_size(&mut self, pipeline_id: PipelineId, layer_id: LayerId, new_size: Size2D, epoch: Epoch) { + let page_window = self.page_window(); let (ask, move): (bool, bool) = match self.compositor_layer { Some(ref mut layer) => { - let window_size = &self.window_size; - let world_zoom = self.world_zoom; - let page_window = Size2D(window_size.width as f32 / world_zoom, - window_size.height as f32 / world_zoom); layer.resize(pipeline_id, layer_id, new_size, page_window, epoch); let move = self.fragment_point.take().map_or(false, |point| { layer.move(pipeline_id, layer_id, point, page_window) @@ -503,13 +501,9 @@ impl IOCompositor { pipeline_id: PipelineId, layer_id: LayerId, point: Point2D) { - let world_zoom = self.world_zoom; - let page_window = Size2D(self.window_size.width as f32 / world_zoom, - self.window_size.height as f32 / world_zoom); - + let page_window = self.page_window(); let (ask, move): (bool, bool) = match self.compositor_layer { Some(ref mut layer) if layer.pipeline.id == pipeline_id && !layer.hidden => { - (true, layer.move(pipeline_id, layer_id, point, page_window)) } Some(_) | None => { @@ -581,15 +575,21 @@ impl IOCompositor { } fn on_resize_window_event(&mut self, width: uint, height: uint) { - let new_size = Size2D(width, height); + let new_size: TypedSize2D = TypedSize2D(width, height); if self.window_size != new_size { debug!("osmain: window resized to {:u}x{:u}", width, height); self.window_size = new_size; let ConstellationChan(ref chan) = self.constellation_chan; - chan.send(ResizedWindowMsg(new_size)) + chan.send(ResizedWindowMsg(new_size.to_untyped())) } else { debug!("osmain: dropping window resize since size is still {:u}x{:u}", width, height); } + // A size change could also mean a resolution change. + let new_hidpi_factor = self.window.hidpi_factor(); + if self.hidpi_factor != new_hidpi_factor { + self.hidpi_factor = new_hidpi_factor; + self.update_zoom_transform(); + } } fn on_load_url_window_event(&mut self, url_string: String) { @@ -606,31 +606,32 @@ impl IOCompositor { } fn on_mouse_window_event_class(&self, mouse_window_event: MouseWindowEvent) { - let world_zoom = self.world_zoom; + let scale = self.device_pixels_per_page_px(); let point = match mouse_window_event { - MouseWindowClickEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom), - MouseWindowMouseDownEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom), - MouseWindowMouseUpEvent(_, p) => Point2D(p.x / world_zoom, p.y / world_zoom), + MouseWindowClickEvent(_, p) => p / scale, + MouseWindowMouseDownEvent(_, p) => p / scale, + MouseWindowMouseUpEvent(_, p) => p / scale, }; for layer in self.compositor_layer.iter() { layer.send_mouse_event(mouse_window_event, point); } } - fn on_mouse_window_move_event_class(&self, cursor: Point2D) { + fn on_mouse_window_move_event_class(&self, cursor: TypedPoint2D) { + let scale = self.device_pixels_per_page_px(); for layer in self.compositor_layer.iter() { - layer.send_mouse_move_event(cursor); + layer.send_mouse_move_event(cursor / scale); } } - fn on_scroll_window_event(&mut self, delta: Point2D, cursor: Point2D) { - let world_zoom = self.world_zoom; + fn on_scroll_window_event(&mut self, + delta: TypedPoint2D, + cursor: TypedPoint2D) { + let scale = self.device_pixels_per_page_px(); // TODO: modify delta to snap scroll to pixels. - let page_delta = Point2D(delta.x as f32 / world_zoom, delta.y as f32 / world_zoom); - let page_cursor: Point2D = Point2D(cursor.x as f32 / world_zoom, - cursor.y as f32 / world_zoom); - let page_window = Size2D(self.window_size.width as f32 / world_zoom, - self.window_size.height as f32 / world_zoom); + let page_delta = delta / scale; + let page_cursor = cursor.as_f32() / scale; + let page_window = self.page_window(); let mut scroll = false; for layer in self.compositor_layer.mut_iter() { scroll = layer.handle_scroll_event(page_delta, page_cursor, page_window) || scroll; @@ -639,27 +640,45 @@ impl IOCompositor { self.ask_for_tiles(); } + fn device_pixels_per_screen_px(&self) -> ScaleFactor { + match self.opts.device_pixels_per_px { + Some(device_pixels_per_px) => device_pixels_per_px, + None => match self.opts.output_file { + Some(_) => ScaleFactor(1.0), + None => self.hidpi_factor + } + } + } + + fn device_pixels_per_page_px(&self) -> ScaleFactor { + self.world_zoom * self.device_pixels_per_screen_px() + } + + fn update_zoom_transform(&mut self) { + let scale = self.device_pixels_per_page_px(); + self.root_layer.common.borrow_mut().set_transform(identity().scale(scale.get(), scale.get(), 1f32)); + } + fn on_zoom_window_event(&mut self, magnification: f32) { self.zoom_action = true; self.zoom_time = precise_time_s(); let old_world_zoom = self.world_zoom; - let window_size = &self.window_size; + let window_size = self.window_size.as_f32(); // Determine zoom amount - self.world_zoom = (self.world_zoom * magnification).max(1.0); + self.world_zoom = ScaleFactor((self.world_zoom.get() * magnification).max(1.0)); let world_zoom = self.world_zoom; - { - self.root_layer.common.borrow_mut().set_transform(identity().scale(world_zoom, world_zoom, 1f32)); - } + self.update_zoom_transform(); // Scroll as needed - let page_delta = Point2D(window_size.width as f32 * (1.0 / world_zoom - 1.0 / old_world_zoom) * 0.5, - window_size.height as f32 * (1.0 / world_zoom - 1.0 / old_world_zoom) * 0.5); + let page_delta = TypedPoint2D( + window_size.width.get() * (world_zoom.inv() - old_world_zoom.inv()).get() * 0.5, + window_size.height.get() * (world_zoom.inv() - old_world_zoom.inv()).get() * 0.5); // TODO: modify delta to snap scroll to pixels. - let page_cursor = Point2D(-1f32, -1f32); // Make sure this hits the base layer - let page_window = Size2D(window_size.width as f32 / world_zoom, - window_size.height as f32 / world_zoom); + let page_cursor = TypedPoint2D(-1f32, -1f32); // Make sure this hits the base layer + let page_window = self.page_window(); + for layer in self.compositor_layer.mut_iter() { layer.handle_scroll_event(page_delta, page_cursor, page_window); } @@ -678,15 +697,14 @@ impl IOCompositor { /// Get BufferRequests from each layer. fn ask_for_tiles(&mut self) { - let world_zoom = self.world_zoom; - let window_size_page = Size2D(self.window_size.width as f32 / world_zoom, - self.window_size.height as f32 / world_zoom); + let scale = self.device_pixels_per_page_px(); + let page_window = self.page_window(); for layer in self.compositor_layer.mut_iter() { if !layer.hidden { - let rect = Rect(Point2D(0f32, 0f32), window_size_page); + let rect = Rect(Point2D(0f32, 0f32), page_window.to_untyped()); let recomposite = layer.get_buffer_request(&self.graphics_context, rect, - world_zoom) || + scale.get()) || self.recomposite; self.recomposite = recomposite; } else { @@ -699,7 +717,7 @@ impl IOCompositor { profile(time::CompositingCategory, self.profiler_chan.clone(), || { debug!("compositor: compositing"); // Adjust the layer dimensions as necessary to correspond to the size of the window. - self.scene.size = self.window.size(); + self.scene.size = self.window.size().to_untyped(); // Render the scene. match self.compositor_layer { Some(ref mut layer) => { @@ -717,7 +735,7 @@ impl IOCompositor { // self.window.present()) as OpenGL ES 2 does not have glReadBuffer(). if self.load_complete && self.ready_state == FinishedLoading && self.opts.output_file.is_some() { - let (width, height) = (self.window_size.width as uint, self.window_size.height as uint); + let (width, height) = (self.window_size.width.get(), self.window_size.height.get()); let path = from_str::(self.opts.output_file.get_ref().as_slice()).unwrap(); let mut pixels = gl2::read_pixels(0, 0, width as gl2::GLsizei, diff --git a/servo/src/components/main/compositing/compositor_layer.rs b/servo/src/components/main/compositing/compositor_layer.rs index 42a918fda3f9..91a6b7126bb3 100644 --- a/servo/src/components/main/compositing/compositor_layer.rs +++ b/servo/src/components/main/compositing/compositor_layer.rs @@ -8,10 +8,11 @@ use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEve use windowing::{MouseWindowMouseUpEvent}; use azure::azure_hl::Color; +use geom::length::Length; use geom::matrix::identity; -use geom::point::Point2D; -use geom::rect::Rect; -use geom::size::Size2D; +use geom::point::{Point2D, TypedPoint2D}; +use geom::rect::{Rect, TypedRect}; +use geom::size::{Size2D, TypedSize2D}; use gfx::render_task::{ReRenderMsg, UnusedBufferMsg}; use gfx; use layers::layers::{ContainerLayerKind, ContainerLayer, Flip, NoFlip, TextureLayer}; @@ -23,6 +24,7 @@ use script::script_task::{ScriptChan, SendEventMsg}; use servo_msg::compositor_msg::{Epoch, FixedPosition, LayerBuffer, LayerBufferSet, LayerId}; use servo_msg::compositor_msg::{ScrollPolicy, Tile}; use servo_msg::constellation_msg::PipelineId; +use servo_util::geometry::PagePx; use std::rc::Rc; #[cfg(target_os="macos")] @@ -59,7 +61,7 @@ pub struct CompositorLayer { /// The offset of the page due to scrolling. (0,0) is when the window sees the /// top left corner of the page. - pub scroll_offset: Point2D, + pub scroll_offset: TypedPoint2D, /// This layer's children. These could be iframes or any element which /// differs in scroll behavior from its parent. Each is associated with a @@ -169,7 +171,7 @@ impl CompositorLayer { id: layer_id, bounds: bounds, page_size: page_size, - scroll_offset: Point2D(0f32, 0f32), + scroll_offset: TypedPoint2D(0f32, 0f32), children: vec!(), quadtree: match page_size { None => NoTree(tile_size, Some(MAX_TILE_MEMORY_PER_LAYER)), @@ -202,7 +204,7 @@ impl CompositorLayer { id: LayerId::null(), bounds: Rect(Point2D(0f32, 0f32), page_size), page_size: Some(page_size), - scroll_offset: Point2D(0f32, 0f32), + scroll_offset: TypedPoint2D(0f32, 0f32), children: vec!(), quadtree: NoTree(tile_size, Some(MAX_TILE_MEMORY_PER_LAYER)), root_layer: Rc::new(ContainerLayer()), @@ -285,9 +287,9 @@ impl CompositorLayer { /// mouse is over child layers first. If a layer successfully scrolled, returns true; otherwise /// returns false, so a parent layer can scroll instead. pub fn handle_scroll_event(&mut self, - delta: Point2D, - cursor: Point2D, - window_size: Size2D) + delta: TypedPoint2D, + cursor: TypedPoint2D, + window_size: TypedSize2D) -> bool { // If this layer is hidden, neither it nor its children will scroll. if self.hidden { @@ -308,6 +310,7 @@ impl CompositorLayer { error!("CompositorLayer: unable to perform cursor hit test for layer"); } Some(rect) => { + let rect: TypedRect = Rect::from_untyped(&rect); if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width && cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height && child.child.handle_scroll_event(delta, @@ -329,12 +332,17 @@ impl CompositorLayer { Some(size) => size, None => fail!("CompositorLayer: tried to scroll with no page size set"), }; - let min_x = (window_size.width - page_size.width).min(0.0); - self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0); - let min_y = (window_size.height - page_size.height).min(0.0); - self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0); - if old_origin - self.scroll_offset == Point2D(0f32, 0f32) { + let window_size = window_size.to_untyped(); + let scroll_offset = self.scroll_offset.to_untyped(); + + let min_x = (window_size.width - page_size.width).min(0.0); + self.scroll_offset.x = Length(scroll_offset.x.clamp(&min_x, &0.0)); + + let min_y = (window_size.height - page_size.height).min(0.0); + self.scroll_offset.y = Length(scroll_offset.y.clamp(&min_y, &0.0)); + + if old_origin - self.scroll_offset == TypedPoint2D(0f32, 0f32) { return false } @@ -358,7 +366,7 @@ impl CompositorLayer { /// Actually scrolls the descendants of a layer that scroll. This is called by /// `handle_scroll_event` above when it determines that a layer wants to scroll. - fn scroll(&mut self, scroll_offset: Point2D) -> bool { + fn scroll(&mut self, scroll_offset: TypedPoint2D) -> bool { let mut result = false; // Only scroll this layer if it's not fixed-positioned. @@ -367,7 +375,7 @@ impl CompositorLayer { self.scroll_offset = scroll_offset; self.root_layer.common.borrow_mut().set_transform( - identity().translate(self.scroll_offset.x, self.scroll_offset.y, 0.0)); + identity().translate(self.scroll_offset.x.get(), self.scroll_offset.y.get(), 0.0)); result = true } @@ -382,7 +390,7 @@ impl CompositorLayer { // Takes in a MouseWindowEvent, determines if it should be passed to children, and // sends the event off to the appropriate pipeline. NB: the cursor position is in // page coordinates. - pub fn send_mouse_event(&self, event: MouseWindowEvent, cursor: Point2D) { + pub fn send_mouse_event(&self, event: MouseWindowEvent, cursor: TypedPoint2D) { let cursor = cursor - self.scroll_offset; for child in self.children.iter().filter(|&x| !x.child.hidden) { match *child.container.scissor.borrow() { @@ -390,6 +398,7 @@ impl CompositorLayer { error!("CompositorLayer: unable to perform cursor hit test for layer"); } Some(rect) => { + let rect: TypedRect = Rect::from_untyped(&rect); if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width && cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height { child.child.send_mouse_event(event, cursor - rect.origin); @@ -401,16 +410,16 @@ impl CompositorLayer { // This mouse event is mine! let message = match event { - MouseWindowClickEvent(button, _) => ClickEvent(button, cursor), - MouseWindowMouseDownEvent(button, _) => MouseDownEvent(button, cursor), - MouseWindowMouseUpEvent(button, _) => MouseUpEvent(button, cursor), + MouseWindowClickEvent(button, _) => ClickEvent(button, cursor.to_untyped()), + MouseWindowMouseDownEvent(button, _) => MouseDownEvent(button, cursor.to_untyped()), + MouseWindowMouseUpEvent(button, _) => MouseUpEvent(button, cursor.to_untyped()), }; let ScriptChan(ref chan) = self.pipeline.script_chan; let _ = chan.send_opt(SendEventMsg(self.pipeline.id.clone(), message)); } - pub fn send_mouse_move_event(&self, cursor: Point2D) { - let message = MouseMoveEvent(cursor); + pub fn send_mouse_move_event(&self, cursor: TypedPoint2D) { + let message = MouseMoveEvent(cursor.to_untyped()); let ScriptChan(ref chan) = self.pipeline.script_chan; let _ = chan.send_opt(SendEventMsg(self.pipeline.id.clone(), message)); } @@ -453,8 +462,9 @@ impl CompositorLayer { match *x.container.scissor.borrow() { Some(scissor) => { let mut new_rect = window_rect; - new_rect.origin.x = new_rect.origin.x - x.child.scroll_offset.x; - new_rect.origin.y = new_rect.origin.y - x.child.scroll_offset.y; + let offset = x.child.scroll_offset.to_untyped(); + new_rect.origin.x = new_rect.origin.x - offset.x; + new_rect.origin.y = new_rect.origin.y - offset.y; match new_rect.intersection(&scissor) { Some(new_rect) => { // Child layers act as if they are rendered at (0,0), so we @@ -535,7 +545,7 @@ impl CompositorLayer { pipeline_id: PipelineId, layer_id: LayerId, new_size: Size2D, - window_size: Size2D, + window_size: TypedSize2D, epoch: Epoch) -> bool { debug!("compositor_layer: starting resize()"); @@ -562,7 +572,7 @@ impl CompositorLayer { } // Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position // to make sure the scroll isn't propagated downwards. - self.handle_scroll_event(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), window_size); + self.handle_scroll_event(TypedPoint2D(0f32, 0f32), TypedPoint2D(-1f32, -1f32), window_size); self.hidden = false; self.set_occlusions(); true @@ -572,7 +582,7 @@ impl CompositorLayer { pipeline_id: PipelineId, layer_id: LayerId, origin: Point2D, - window_size: Size2D) + window_size: TypedSize2D) -> bool { // Search children for the right layer to move. if self.pipeline.id != pipeline_id || self.id != layer_id { @@ -587,20 +597,23 @@ impl CompositorLayer { // Scroll this layer! let old_origin = self.scroll_offset; - self.scroll_offset = Point2D(0f32, 0f32) - origin; + self.scroll_offset = Point2D::from_untyped(&(origin * -1.0)); // bounds checking let page_size = match self.page_size { Some(size) => size, None => fail!("CompositorLayer: tried to scroll with no page size set"), }; + let window_size = window_size.to_untyped(); + let scroll_offset = self.scroll_offset.to_untyped(); + let min_x = (window_size.width - page_size.width).min(0.0); - self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0); + self.scroll_offset.x = Length(scroll_offset.x.clamp(&min_x, &0.0)); let min_y = (window_size.height - page_size.height).min(0.0); - self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0); + self.scroll_offset.y = Length(scroll_offset.y.clamp(&min_y, &0.0)); // check to see if we scrolled - if old_origin - self.scroll_offset == Point2D(0f32, 0f32) { + if old_origin - self.scroll_offset == TypedPoint2D(0f32, 0f32) { return false; } @@ -669,9 +682,10 @@ impl CompositorLayer { Some(scissor) => { // Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the // cursor position to make sure the scroll isn't propagated downwards. - child.handle_scroll_event(Point2D(0f32, 0f32), - Point2D(-1f32, -1f32), - scissor.size); + let size: TypedSize2D = Size2D::from_untyped(&scissor.size); + child.handle_scroll_event(TypedPoint2D(0f32, 0f32), + TypedPoint2D(-1f32, -1f32), + size); child.hidden = false; } None => {} // Nothing to do diff --git a/servo/src/components/main/platform/common/glfw_windowing.rs b/servo/src/components/main/platform/common/glfw_windowing.rs index 68dad142c04b..0ac70ecbf40c 100644 --- a/servo/src/components/main/platform/common/glfw_windowing.rs +++ b/servo/src/components/main/platform/common/glfw_windowing.rs @@ -19,10 +19,12 @@ use std::cell::{Cell, RefCell}; use std::comm::Receiver; use std::rc::Rc; -use geom::point::Point2D; -use geom::size::Size2D; +use geom::point::{Point2D, TypedPoint2D}; +use geom::scale_factor::ScaleFactor; +use geom::size::TypedSize2D; use servo_msg::compositor_msg::{IdleRenderState, RenderState, RenderingRenderState}; use servo_msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayout, ReadyState}; +use servo_util::geometry::{ScreenPx, DevicePixel}; use glfw; use glfw::Context; @@ -144,9 +146,9 @@ impl WindowMethods for Window { } /// Returns the size of the window. - fn size(&self) -> Size2D { + fn size(&self) -> TypedSize2D { let (width, height) = self.glfw_window.get_framebuffer_size(); - Size2D(width as f32, height as f32) + TypedSize2D(width as f32, height as f32) } /// Presents the window to the screen (perhaps by page flipping). @@ -193,10 +195,10 @@ impl WindowMethods for Window { self.update_window_title() } - fn hidpi_factor(&self) -> f32 { + fn hidpi_factor(&self) -> ScaleFactor { let (backing_size, _) = self.glfw_window.get_framebuffer_size(); let (window_size, _) = self.glfw_window.get_size(); - (backing_size as f32) / (window_size as f32) + ScaleFactor((backing_size as f32) / (window_size as f32)) } } @@ -227,7 +229,8 @@ impl Window { } }, glfw::CursorPosEvent(xpos, ypos) => { - self.event_queue.borrow_mut().push(MouseWindowMoveEventClass(Point2D(xpos as f32, ypos as f32))); + self.event_queue.borrow_mut().push( + MouseWindowMoveEventClass(TypedPoint2D(xpos as f32, ypos as f32))); }, glfw::ScrollEvent(xpos, ypos) => { let dx = (xpos as f32) * 30.0; @@ -241,7 +244,8 @@ impl Window { let x = x as f32 * hidpi; let y = y as f32 * hidpi; - self.event_queue.borrow_mut().push(ScrollWindowEvent(Point2D(dx, dy), Point2D(x as i32, y as i32))); + self.event_queue.borrow_mut().push(ScrollWindowEvent(TypedPoint2D(dx, dy), + TypedPoint2D(x as i32, y as i32))); }, _ => {} } @@ -307,7 +311,7 @@ impl Window { glfw::Press => { self.mouse_down_point.set(Point2D(x, y)); self.mouse_down_button.set(Some(button)); - MouseWindowMouseDownEvent(button as uint, Point2D(x as f32, y as f32)) + MouseWindowMouseDownEvent(button as uint, TypedPoint2D(x as f32, y as f32)) } glfw::Release => { match self.mouse_down_button.get() { @@ -318,13 +322,13 @@ impl Window { pixel_dist.y * pixel_dist.y) as f64).sqrt(); if pixel_dist < max_pixel_dist { let click_event = MouseWindowClickEvent(button as uint, - Point2D(x as f32, y as f32)); + TypedPoint2D(x as f32, y as f32)); self.event_queue.borrow_mut().push(MouseWindowEventClass(click_event)); } } Some(_) => (), } - MouseWindowMouseUpEvent(button as uint, Point2D(x as f32, y as f32)) + MouseWindowMouseUpEvent(button as uint, TypedPoint2D(x as f32, y as f32)) } _ => fail!("I cannot recognize the type of mouse action that occured. :-(") }; diff --git a/servo/src/components/main/platform/common/glut_windowing.rs b/servo/src/components/main/platform/common/glut_windowing.rs index 26c0b7765756..4465bef791f8 100644 --- a/servo/src/components/main/platform/common/glut_windowing.rs +++ b/servo/src/components/main/platform/common/glut_windowing.rs @@ -14,10 +14,12 @@ use alert::{Alert, AlertMethods}; use libc::{c_int, c_uchar}; use std::cell::{Cell, RefCell}; use std::rc::Rc; -use geom::point::Point2D; -use geom::size::Size2D; +use geom::point::{Point2D, TypedPoint2D}; +use geom::scale_factor::ScaleFactor; +use geom::size::TypedSize2D; use servo_msg::compositor_msg::{IdleRenderState, RenderState, RenderingRenderState}; use servo_msg::compositor_msg::{FinishedLoading, Blank, ReadyState}; +use servo_util::geometry::{ScreenPx, DevicePixel}; use glut::glut::{ACTIVE_SHIFT, DOUBLE, WindowHeight}; use glut::glut::WindowWidth; @@ -118,11 +120,15 @@ impl WindowMethods for Window { match button { 3 => { let tmp = local_window(); - tmp.event_queue.borrow_mut().push(ScrollWindowEvent(Point2D(0.0, 5.0 as f32), Point2D(0.0 as i32, 5.0 as i32))); + tmp.event_queue.borrow_mut().push(ScrollWindowEvent( + TypedPoint2D(0.0, 5.0 as f32), + TypedPoint2D(0.0 as i32, 5.0 as i32))); }, 4 => { let tmp = local_window(); - tmp.event_queue.borrow_mut().push(ScrollWindowEvent(Point2D(0.0, -5.0 as f32), Point2D(0.0 as i32, -5.0 as i32))); + tmp.event_queue.borrow_mut().push(ScrollWindowEvent( + TypedPoint2D(0.0, -5.0 as f32), + TypedPoint2D(0.0 as i32, -5.0 as i32))); }, _ => {} } @@ -139,8 +145,8 @@ impl WindowMethods for Window { } /// Returns the size of the window. - fn size(&self) -> Size2D { - Size2D(glut::get(WindowWidth) as f32, glut::get(WindowHeight) as f32) + fn size(&self) -> TypedSize2D { + TypedSize2D(glut::get(WindowWidth) as f32, glut::get(WindowHeight) as f32) } /// Presents the window to the screen (perhaps by page flipping). @@ -179,9 +185,9 @@ impl WindowMethods for Window { //self.update_window_title() } - fn hidpi_factor(&self) -> f32 { + fn hidpi_factor(&self) -> ScaleFactor { //FIXME: Do nothing in GLUT now. - 1f32 + ScaleFactor(1.0) } } @@ -218,8 +224,10 @@ impl Window { 42 => self.load_url(), 43 => self.event_queue.borrow_mut().push(ZoomWindowEvent(1.1)), 45 => self.event_queue.borrow_mut().push(ZoomWindowEvent(0.909090909)), - 56 => self.event_queue.borrow_mut().push(ScrollWindowEvent(Point2D(0.0, 5.0 as f32), Point2D(0.0 as i32, 5.0 as i32))), - 50 => self.event_queue.borrow_mut().push(ScrollWindowEvent(Point2D(0.0, -5.0 as f32), Point2D(0.0 as i32, -5.0 as i32))), + 56 => self.event_queue.borrow_mut().push(ScrollWindowEvent(TypedPoint2D(0.0, 5.0 as f32), + TypedPoint2D(0.0 as i32, 5.0 as i32))), + 50 => self.event_queue.borrow_mut().push(ScrollWindowEvent(TypedPoint2D(0.0, -5.0 as f32), + TypedPoint2D(0.0 as i32, -5.0 as i32))), 127 => { if (modifiers & ACTIVE_SHIFT) != 0 { self.event_queue.borrow_mut().push(NavigationWindowEvent(Forward)); @@ -240,7 +248,7 @@ impl Window { glut::MOUSE_DOWN => { self.mouse_down_point.set(Point2D(x, y)); self.mouse_down_button.set(button); - MouseWindowMouseDownEvent(button as uint, Point2D(x as f32, y as f32)) + MouseWindowMouseDownEvent(button as uint, TypedPoint2D(x as f32, y as f32)) } glut::MOUSE_UP => { if self.mouse_down_button.get() == button { @@ -249,11 +257,11 @@ impl Window { pixel_dist.y * pixel_dist.y) as f32).sqrt(); if pixel_dist < max_pixel_dist { let click_event = MouseWindowClickEvent(button as uint, - Point2D(x as f32, y as f32)); + TypedPoint2D(x as f32, y as f32)); self.event_queue.borrow_mut().push(MouseWindowEventClass(click_event)); } } - MouseWindowMouseUpEvent(button as uint, Point2D(x as f32, y as f32)) + MouseWindowMouseUpEvent(button as uint, TypedPoint2D(x as f32, y as f32)) } _ => fail!("I cannot recognize the type of mouse action that occured. :-(") }; diff --git a/servo/src/components/main/windowing.rs b/servo/src/components/main/windowing.rs index 23767085bbb0..1cded10aaffb 100644 --- a/servo/src/components/main/windowing.rs +++ b/servo/src/components/main/windowing.rs @@ -4,15 +4,17 @@ //! Abstract windowing methods. The concrete implementations of these can be found in `platform/`. -use geom::point::Point2D; -use geom::size::Size2D; +use geom::point::TypedPoint2D; +use geom::scale_factor::ScaleFactor; +use geom::size::TypedSize2D; use servo_msg::compositor_msg::{ReadyState, RenderState}; +use servo_util::geometry::{ScreenPx, DevicePixel}; use std::rc::Rc; pub enum MouseWindowEvent { - MouseWindowClickEvent(uint, Point2D), - MouseWindowMouseDownEvent(uint, Point2D), - MouseWindowMouseUpEvent(uint, Point2D), + MouseWindowClickEvent(uint, TypedPoint2D), + MouseWindowMouseDownEvent(uint, TypedPoint2D), + MouseWindowMouseUpEvent(uint, TypedPoint2D), } pub enum WindowNavigateMsg { @@ -36,9 +38,9 @@ pub enum WindowEvent { /// Sent when a mouse hit test is to be performed. MouseWindowEventClass(MouseWindowEvent), /// Sent when a mouse move. - MouseWindowMoveEventClass(Point2D), + MouseWindowMoveEventClass(TypedPoint2D), /// Sent when the user scrolls. Includes the current cursor position. - ScrollWindowEvent(Point2D, Point2D), + ScrollWindowEvent(TypedPoint2D, TypedPoint2D), /// Sent when the user zooms. ZoomWindowEvent(f32), /// Sent when the user uses chrome navigation (i.e. backspace or shift-backspace). @@ -58,7 +60,7 @@ pub trait WindowMethods { /// Creates a new window. fn new(app: &A, is_foreground: bool) -> Rc; /// Returns the size of the window. - fn size(&self) -> Size2D; + fn size(&self) -> TypedSize2D; /// Presents the window to the screen (perhaps by page flipping). fn present(&self); @@ -71,6 +73,6 @@ pub trait WindowMethods { fn set_render_state(&self, render_state: RenderState); /// Returns the hidpi factor of the monitor. - fn hidpi_factor(&self) -> f32; + fn hidpi_factor(&self) -> ScaleFactor; } diff --git a/servo/src/components/util/geometry.rs b/servo/src/components/util/geometry.rs index 0e6fd2acf8c7..1b9915118be9 100644 --- a/servo/src/components/util/geometry.rs +++ b/servo/src/components/util/geometry.rs @@ -10,9 +10,41 @@ use std::default::Default; use std::num::{NumCast, One, Zero}; use std::fmt; +// Units for use with geom::length and geom::scale_factor. + +/// One hardware pixel. +/// +/// This unit corresponds to the smallest addressable element of the display hardware. +pub enum DevicePixel {} + +/// A normalized "pixel" at the default resolution for the display. +/// +/// Like the CSS "px" unit, the exact physical size of this unit may vary between devices, but it +/// should approximate a device-independent reference length. This unit corresponds to Android's +/// "density-independent pixel" (dip), Mac OS X's "point", and Windows "device-independent pixel." +/// +/// The relationship between DevicePixel and ScreenPx is defined by the OS. On most low-dpi +/// screens, one ScreenPx is equal to one DevicePixel. But on high-density screens it can be +/// some larger number. For example, by default on Apple "retina" displays, one ScreenPx equals +/// two DevicePixels. On Android "MDPI" displays, one ScreenPx equals 1.5 device pixels. +/// +/// The ratio between ScreenPx and DevicePixel for a given display be found by calling +/// `servo::windowing::WindowMethods::hidpi_factor`. +pub enum ScreenPx {} + +/// One CSS "px" in the root coordinate system for the content document. +/// +/// +/// PagePx is equal to ScreenPx multiplied by a "zoom" factor controlled by the user. At the +/// default zoom level of 100%, one PagePx is equal to one ScreenPx. However, if the document +/// is zoomed in or out then this scale may be larger or smaller. +pub enum PagePx {} + // An Au is an "App Unit" and represents 1/60th of a CSS pixel. It was // originally proposed in 2002 as a standard unit of measure in Gecko. // See https://bugzilla.mozilla.org/show_bug.cgi?id=177805 for more info. +// +// FIXME: Implement Au using Length and ScaleFactor instead of a custom type. #[deriving(Clone, Eq, Ord, Zero)] pub struct Au(pub i32); diff --git a/servo/src/components/util/opts.rs b/servo/src/components/util/opts.rs index b17d2d196c2d..166ff0a9a9a4 100644 --- a/servo/src/components/util/opts.rs +++ b/servo/src/components/util/opts.rs @@ -5,8 +5,11 @@ //! Configuration options for a single run of the servo application. Created //! from command line arguments. +use geometry::{DevicePixel, ScreenPx}; + use azure::azure_hl::{BackendType, CairoBackend, CoreGraphicsBackend}; use azure::azure_hl::{CoreGraphicsAcceleratedBackend, Direct2DBackend, SkiaBackend}; +use geom::scale_factor::ScaleFactor; use getopts; use std::cmp; use std::io; @@ -36,7 +39,7 @@ pub struct Opts { /// The ratio of device pixels per px at the default scale. If unspecified, will use the /// platform default setting. - pub device_pixels_per_px: Option, + pub device_pixels_per_px: Option>, /// `None` to disable the profiler or `Some` with an interval in seconds to enable it and cause /// it to produce output on that interval (`-p`). @@ -136,7 +139,7 @@ pub fn from_cmdline_args(args: &[String]) -> Option { }; let device_pixels_per_px = opt_match.opt_str("device-pixel-ratio").map(|dppx_str| - from_str(dppx_str.as_slice()).unwrap() + ScaleFactor(from_str(dppx_str.as_slice()).unwrap()) ); let n_render_threads: uint = match opt_match.opt_str("t") {