From 188efcc17a100c83ea3c7c445b702dd605d05dcb Mon Sep 17 00:00:00 2001 From: Cameron Zwarich Date: Wed, 9 Jul 2014 14:29:27 -0700 Subject: [PATCH] servo: Merge #2789 - Move compositor event handling code to a new `events` module (from zwarich:compositor-events) Source-Repo: https://github.com/servo/servo Source-Revision: cb7828c76e29aa6a13cc95275b5b3847bf9fa2d4 --- .../src/components/compositing/compositing.rs | 1 + .../src/components/compositing/compositor.rs | 33 +-- .../components/compositing/compositor_data.rs | 213 +---------------- servo/src/components/compositing/events.rs | 218 ++++++++++++++++++ 4 files changed, 242 insertions(+), 223 deletions(-) create mode 100644 servo/src/components/compositing/events.rs diff --git a/servo/src/components/compositing/compositing.rs b/servo/src/components/compositing/compositing.rs index b944567549d3..b7700ef51c1e 100644 --- a/servo/src/components/compositing/compositing.rs +++ b/servo/src/components/compositing/compositing.rs @@ -50,6 +50,7 @@ pub use constellation::Constellation; pub mod compositor_task; mod compositor_data; +mod events; mod compositor; mod headless; diff --git a/servo/src/components/compositing/compositor.rs b/servo/src/components/compositing/compositor.rs index bd76573a0b09..b18682e0b24f 100644 --- a/servo/src/components/compositing/compositor.rs +++ b/servo/src/components/compositing/compositor.rs @@ -8,6 +8,7 @@ use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpda use compositor_task::{SetLayerClipRect, Paint, ScrollFragmentPoint, LoadComplete}; use compositor_task::{ShutdownComplete, ChangeRenderState}; use constellation::SendableFrameTree; +use events; use pipeline::CompositionPipeline; use platform::{Application, Window}; use windowing; @@ -444,11 +445,11 @@ impl IOCompositor { let needs_recomposite = match self.scene.root { Some(ref mut root_layer) => { self.fragment_point.take().map_or(false, |fragment_point| { - CompositorData::move(root_layer.clone(), - pipeline_id, - layer_id, - fragment_point, - page_window) + events::move(root_layer.clone(), + pipeline_id, + layer_id, + fragment_point, + page_window) }) } None => fail!("Compositor: Tried to scroll to fragment without root layer."), @@ -528,7 +529,7 @@ impl IOCompositor { Some(ref layer) if layer.extra_data.borrow().pipeline.id == pipeline_id && !layer.extra_data.borrow().hidden => { (true, - CompositorData::move(layer.clone(), pipeline_id, layer_id, point, page_window)) + events::move(layer.clone(), pipeline_id, layer_id, point, page_window)) } Some(_) | None => { self.fragment_point = Some(point); @@ -640,14 +641,14 @@ impl IOCompositor { MouseWindowMouseUpEvent(_, p) => p / scale, }; for layer in self.scene.root.iter() { - CompositorData::send_mouse_event(layer.clone(), mouse_window_event, point); + events::send_mouse_event(layer.clone(), mouse_window_event, point); } } fn on_mouse_window_move_event_class(&self, cursor: TypedPoint2D) { let scale = self.device_pixels_per_page_px(); for layer in self.scene.root.iter() { - CompositorData::send_mouse_move_event(layer.clone(), cursor / scale); + events::send_mouse_move_event(layer.clone(), cursor / scale); } } @@ -662,10 +663,10 @@ impl IOCompositor { let mut scroll = false; match self.scene.root { Some(ref mut layer) => { - scroll = CompositorData::handle_scroll_event(layer.clone(), - page_delta, - page_cursor, - page_window) || scroll; + scroll = events::handle_scroll_event(layer.clone(), + page_delta, + page_cursor, + page_window) || scroll; } None => { } } @@ -719,10 +720,10 @@ impl IOCompositor { match self.scene.root { Some(ref mut layer) => { - CompositorData::handle_scroll_event(layer.clone(), - page_delta, - page_cursor, - page_window); + events::handle_scroll_event(layer.clone(), + page_delta, + page_cursor, + page_window); } None => { } } diff --git a/servo/src/components/compositing/compositor_data.rs b/servo/src/components/compositing/compositor_data.rs index af845f5312a5..ac5adc53700b 100644 --- a/servo/src/components/compositing/compositor_data.rs +++ b/servo/src/components/compositing/compositor_data.rs @@ -3,23 +3,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use compositor_task::LayerProperties; +use events; use pipeline::CompositionPipeline; -use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent}; -use windowing::{MouseWindowMouseUpEvent}; use azure::azure_hl::Color; -use geom::length::Length; use geom::matrix::identity; use geom::point::{Point2D, TypedPoint2D}; -use geom::rect::{Rect, TypedRect}; +use geom::rect::Rect; use geom::size::{Size2D, TypedSize2D}; use gfx::render_task::{ReRenderMsg, UnusedBufferMsg}; use layers::layers::{Layer, Flip, LayerBuffer, LayerBufferSet, NoFlip, TextureLayer}; use layers::quadtree::{Tile, Normal, Hidden}; use layers::platform::surface::{NativeCompositingGraphicsContext, NativeSurfaceMethods}; use layers::texturegl::{Texture, TextureTarget}; -use script::dom::event::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent}; -use script::script_task::{ScriptChan, SendEventMsg}; use servo_msg::compositor_msg::{Epoch, FixedPosition, LayerId}; use servo_msg::compositor_msg::ScrollPolicy; use servo_msg::constellation_msg::PipelineId; @@ -82,24 +78,6 @@ pub enum WantsScrollEventsFlag { DoesntWantScrollEvents, } -trait Clampable { - fn clamp(&self, mn: &Self, mx: &Self) -> Self; -} - -impl Clampable for f32 { - /// Returns the number constrained within the range `mn <= self <= mx`. - /// If any of the numbers are `NAN` then `NAN` is returned. - #[inline] - fn clamp(&self, mn: &f32, mx: &f32) -> f32 { - match () { - _ if self.is_nan() => *self, - _ if !(*self <= *mx) => *mx, - _ if !(*self >= *mn) => *mn, - _ => *self, - } - } -} - impl CompositorData { pub fn new(pipeline: CompositionPipeline, layer_id: LayerId, @@ -169,141 +147,6 @@ impl CompositorData { Layer::add_child(layer.clone(), new_kid.clone()); } - /// Move the layer's descendants that don't want scroll events and scroll by a relative - /// specified amount in page coordinates. This also takes in a cursor position to see if the - /// 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(layer: Rc>, - delta: TypedPoint2D, - cursor: TypedPoint2D, - window_size: TypedSize2D) - -> bool { - // If this layer is hidden, neither it nor its children will scroll. - if layer.extra_data.borrow().hidden { - return false - } - - // If this layer doesn't want scroll events, neither it nor its children can handle scroll - // events. - if layer.extra_data.borrow().wants_scroll_events != WantsScrollEvents { - return false - } - - // Allow children to scroll. - let cursor = cursor - layer.extra_data.borrow().scroll_offset; - for child in layer.children().iter() { - match child.extra_data.borrow().scissor { - None => { - error!("CompositorData: unable to perform cursor hit test for layer"); - } - Some(rect) => { - let rect: TypedRect = Rect::from_untyped(&rect); - if rect.contains(&cursor) && - CompositorData::handle_scroll_event(child.clone(), - delta, - cursor - rect.origin, - rect.size) { - return true - } - } - } - } - - // This scroll event is mine! - // Scroll this layer! - let old_origin = layer.extra_data.borrow().scroll_offset.clone(); - layer.extra_data.borrow_mut().scroll_offset = old_origin + delta; - - // bounds checking - let page_size = match layer.extra_data.borrow().page_size { - Some(size) => size, - None => fail!("CompositorData: tried to scroll with no page size set"), - }; - - let window_size = window_size.to_untyped(); - let scroll_offset = layer.extra_data.borrow().scroll_offset.to_untyped(); - - let min_x = (window_size.width - page_size.width).min(0.0); - layer.extra_data.borrow_mut().scroll_offset.x = Length(scroll_offset.x.clamp(&min_x, &0.0)); - - let min_y = (window_size.height - page_size.height).min(0.0); - layer.extra_data.borrow_mut().scroll_offset.y = Length(scroll_offset.y.clamp(&min_y, &0.0)); - - if old_origin - layer.extra_data.borrow().scroll_offset == TypedPoint2D(0f32, 0f32) { - return false - } - - let offset = layer.extra_data.borrow().scroll_offset.clone(); - CompositorData::scroll(layer.clone(), offset) - } - - /// 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(layer: Rc>, - scroll_offset: TypedPoint2D) - -> bool { - let mut result = false; - - // Only scroll this layer if it's not fixed-positioned. - if layer.extra_data.borrow().scroll_policy != FixedPosition { - // Scroll this layer! - layer.extra_data.borrow_mut().scroll_offset = scroll_offset; - - let scroll_offset = layer.extra_data.borrow().scroll_offset.clone(); - *layer.transform.borrow_mut() = identity().translate(scroll_offset.x.get(), scroll_offset.y.get(), 0.0); - - result = true - } - - for child in layer.children().iter() { - result = CompositorData::scroll(child.clone(), scroll_offset) || result; - } - - result - } - - // 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(layer: Rc>, - event: MouseWindowEvent, cursor: TypedPoint2D) { - let cursor = cursor - layer.extra_data.borrow().scroll_offset; - for child in layer.children().iter() { - if child.extra_data.borrow().hidden { - continue; - } - - match child.extra_data.borrow().scissor { - None => { - error!("CompositorData: unable to perform cursor hit test for layer"); - } - Some(rect) => { - let rect: TypedRect = Rect::from_untyped(&rect); - if rect.contains(&cursor) { - CompositorData::send_mouse_event(child.clone(), event, cursor - rect.origin); - return; - } - } - } - } - - // This mouse event is mine! - let message = match event { - 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) = layer.extra_data.borrow().pipeline.script_chan; - let _ = chan.send_opt(SendEventMsg(layer.extra_data.borrow().pipeline.id.clone(), message)); - } - - pub fn send_mouse_move_event(layer: Rc>, - cursor: TypedPoint2D) { - let message = MouseMoveEvent(cursor.to_untyped()); - let ScriptChan(ref chan) = layer.extra_data.borrow().pipeline.script_chan; - let _ = chan.send_opt(SendEventMsg(layer.extra_data.borrow().pipeline.id.clone(), message)); - } - // Given the current window size, determine which tiles need to be (re-)rendered and sends them // off the the appropriate renderer. Returns true if and only if the scene should be repainted. pub fn send_buffer_requests_recursively(layer: Rc>, @@ -439,10 +282,10 @@ impl CompositorData { // 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. let size: TypedSize2D = Size2D::from_untyped(&scissor.size); - CompositorData::handle_scroll_event(layer.clone(), - TypedPoint2D(0f32, 0f32), - TypedPoint2D(-1f32, -1f32), - size); + events::handle_scroll_event(layer.clone(), + TypedPoint2D(0f32, 0f32), + TypedPoint2D(-1f32, -1f32), + size); layer.extra_data.borrow_mut().hidden = false; } None => {} // Nothing to do @@ -451,50 +294,6 @@ impl CompositorData { CompositorData::set_occlusions(layer.clone()); } - pub fn move(layer: Rc>, - pipeline_id: PipelineId, - layer_id: LayerId, - origin: Point2D, - window_size: TypedSize2D) - -> bool { - // Search children for the right layer to move. - if layer.extra_data.borrow().pipeline.id != pipeline_id || - layer.extra_data.borrow().id != layer_id { - return layer.children().iter().any(|kid| { - CompositorData::move(kid.clone(), pipeline_id, layer_id, origin, window_size) - }); - } - - if layer.extra_data.borrow().wants_scroll_events != WantsScrollEvents { - return false - } - - // Scroll this layer! - let old_origin = layer.extra_data.borrow().scroll_offset; - layer.extra_data.borrow_mut().scroll_offset = Point2D::from_untyped(&(origin * -1.0)); - - // bounds checking - let page_size = match layer.extra_data.borrow().page_size { - Some(size) => size, - None => fail!("CompositorData: tried to scroll with no page size set"), - }; - let window_size = window_size.to_untyped(); - let scroll_offset = layer.extra_data.borrow().scroll_offset.to_untyped(); - - let min_x = (window_size.width - page_size.width).min(0.0); - layer.extra_data.borrow_mut().scroll_offset.x = Length(scroll_offset.x.clamp(&min_x, &0.0)); - let min_y = (window_size.height - page_size.height).min(0.0); - layer.extra_data.borrow_mut().scroll_offset.y = Length(scroll_offset.y.clamp(&min_y, &0.0)); - - // check to see if we scrolled - if old_origin - layer.extra_data.borrow().scroll_offset == TypedPoint2D(0f32, 0f32) { - return false; - } - - let offset = layer.extra_data.borrow().scroll_offset.clone(); - CompositorData::scroll(layer.clone(), offset) - } - // Returns whether the layer should be vertically flipped. #[cfg(target_os="macos")] fn texture_flip_and_target(cpu_painting: bool, size: Size2D) -> (Flip, TextureTarget) { diff --git a/servo/src/components/compositing/events.rs b/servo/src/components/compositing/events.rs new file mode 100644 index 000000000000..e32cc821902a --- /dev/null +++ b/servo/src/components/compositing/events.rs @@ -0,0 +1,218 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use compositor_data::{CompositorData, WantsScrollEvents}; +use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent}; +use windowing::MouseWindowMouseUpEvent; + +use geom::length::Length; +use geom::matrix::identity; +use geom::point::{Point2D, TypedPoint2D}; +use geom::rect::{Rect, TypedRect}; +use geom::size::TypedSize2D; +use layers::layers::Layer; +use script::dom::event::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent}; +use script::script_task::{ScriptChan, SendEventMsg}; +use servo_msg::compositor_msg::{FixedPosition, LayerId}; +use servo_msg::constellation_msg::PipelineId; +use servo_util::geometry::PagePx; +use std::rc::Rc; + +trait Clampable { + fn clamp(&self, mn: &Self, mx: &Self) -> Self; +} + +impl Clampable for f32 { + /// Returns the number constrained within the range `mn <= self <= mx`. + /// If any of the numbers are `NAN` then `NAN` is returned. + #[inline] + fn clamp(&self, mn: &f32, mx: &f32) -> f32 { + match () { + _ if self.is_nan() => *self, + _ if !(*self <= *mx) => *mx, + _ if !(*self >= *mn) => *mn, + _ => *self, + } + } +} + +/// Move the layer's descendants that don't want scroll events and scroll by a relative +/// specified amount in page coordinates. This also takes in a cursor position to see if the +/// 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(layer: Rc>, + delta: TypedPoint2D, + cursor: TypedPoint2D, + window_size: TypedSize2D) + -> bool { + // If this layer is hidden, neither it nor its children will scroll. + if layer.extra_data.borrow().hidden { + return false + } + + // If this layer doesn't want scroll events, neither it nor its children can handle scroll + // events. + if layer.extra_data.borrow().wants_scroll_events != WantsScrollEvents { + return false + } + + // Allow children to scroll. + let cursor = cursor - layer.extra_data.borrow().scroll_offset; + for child in layer.children().iter() { + match child.extra_data.borrow().scissor { + None => { + error!("CompositorData: unable to perform cursor hit test for layer"); + } + Some(rect) => { + let rect: TypedRect = Rect::from_untyped(&rect); + if rect.contains(&cursor) && + handle_scroll_event(child.clone(), + delta, + cursor - rect.origin, + rect.size) { + return true + } + } + } + } + + // This scroll event is mine! + // Scroll this layer! + let old_origin = layer.extra_data.borrow().scroll_offset.clone(); + layer.extra_data.borrow_mut().scroll_offset = old_origin + delta; + + // bounds checking + let page_size = match layer.extra_data.borrow().page_size { + Some(size) => size, + None => fail!("CompositorData: tried to scroll with no page size set"), + }; + + let window_size = window_size.to_untyped(); + let scroll_offset = layer.extra_data.borrow().scroll_offset.to_untyped(); + + let min_x = (window_size.width - page_size.width).min(0.0); + layer.extra_data.borrow_mut().scroll_offset.x = Length(scroll_offset.x.clamp(&min_x, &0.0)); + + let min_y = (window_size.height - page_size.height).min(0.0); + layer.extra_data.borrow_mut().scroll_offset.y = Length(scroll_offset.y.clamp(&min_y, &0.0)); + + if old_origin - layer.extra_data.borrow().scroll_offset == TypedPoint2D(0f32, 0f32) { + return false + } + + let offset = layer.extra_data.borrow().scroll_offset.clone(); + scroll(layer.clone(), offset) +} + +/// 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(layer: Rc>, + scroll_offset: TypedPoint2D) + -> bool { + let mut result = false; + + // Only scroll this layer if it's not fixed-positioned. + if layer.extra_data.borrow().scroll_policy != FixedPosition { + // Scroll this layer! + layer.extra_data.borrow_mut().scroll_offset = scroll_offset; + + let scroll_offset = layer.extra_data.borrow().scroll_offset.clone(); + *layer.transform.borrow_mut() = identity().translate(scroll_offset.x.get(), scroll_offset.y.get(), 0.0); + + result = true + } + + for child in layer.children().iter() { + result = scroll(child.clone(), scroll_offset) || result; + } + + result +} + +// 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(layer: Rc>, + event: MouseWindowEvent, cursor: TypedPoint2D) { + let cursor = cursor - layer.extra_data.borrow().scroll_offset; + for child in layer.children().iter() { + if child.extra_data.borrow().hidden { + continue; + } + + match child.extra_data.borrow().scissor { + None => { + error!("CompositorData: unable to perform cursor hit test for layer"); + } + Some(rect) => { + let rect: TypedRect = Rect::from_untyped(&rect); + if rect.contains(&cursor) { + send_mouse_event(child.clone(), event, cursor - rect.origin); + return; + } + } + } + } + + // This mouse event is mine! + let message = match event { + 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) = layer.extra_data.borrow().pipeline.script_chan; + let _ = chan.send_opt(SendEventMsg(layer.extra_data.borrow().pipeline.id.clone(), message)); +} + +pub fn send_mouse_move_event(layer: Rc>, + cursor: TypedPoint2D) { + let message = MouseMoveEvent(cursor.to_untyped()); + let ScriptChan(ref chan) = layer.extra_data.borrow().pipeline.script_chan; + let _ = chan.send_opt(SendEventMsg(layer.extra_data.borrow().pipeline.id.clone(), message)); +} + +pub fn move(layer: Rc>, + pipeline_id: PipelineId, + layer_id: LayerId, + origin: Point2D, + window_size: TypedSize2D) + -> bool { + // Search children for the right layer to move. + if layer.extra_data.borrow().pipeline.id != pipeline_id || + layer.extra_data.borrow().id != layer_id { + return layer.children().iter().any(|kid| { + move(kid.clone(), pipeline_id, layer_id, origin, window_size) + }); + } + + if layer.extra_data.borrow().wants_scroll_events != WantsScrollEvents { + return false + } + + // Scroll this layer! + let old_origin = layer.extra_data.borrow().scroll_offset; + layer.extra_data.borrow_mut().scroll_offset = Point2D::from_untyped(&(origin * -1.0)); + + // bounds checking + let page_size = match layer.extra_data.borrow().page_size { + Some(size) => size, + None => fail!("CompositorData: tried to scroll with no page size set"), + }; + let window_size = window_size.to_untyped(); + let scroll_offset = layer.extra_data.borrow().scroll_offset.to_untyped(); + + let min_x = (window_size.width - page_size.width).min(0.0); + layer.extra_data.borrow_mut().scroll_offset.x = Length(scroll_offset.x.clamp(&min_x, &0.0)); + let min_y = (window_size.height - page_size.height).min(0.0); + layer.extra_data.borrow_mut().scroll_offset.y = Length(scroll_offset.y.clamp(&min_y, &0.0)); + + // check to see if we scrolled + if old_origin - layer.extra_data.borrow().scroll_offset == TypedPoint2D(0f32, 0f32) { + return false; + } + + let offset = layer.extra_data.borrow().scroll_offset.clone(); + scroll(layer.clone(), offset) +} +