diff options
author | bors-servo <release+servo@mozilla.com> | 2013-12-16 15:01:17 -0800 |
---|---|---|
committer | bors-servo <release+servo@mozilla.com> | 2013-12-16 15:01:17 -0800 |
commit | 9aeb1fb475a8f03413c05b4b7608556aa781560f (patch) | |
tree | f9805c3d371aa67403645dac31ae634d1f2962c1 /src | |
parent | 1593d2f69509d0315b87c5c35fd46e3a7f9ba2d9 (diff) | |
parent | cba47ba740150c3f1b88a0ce3a4c96cd11b47489 (diff) | |
download | servo-9aeb1fb475a8f03413c05b4b7608556aa781560f.tar.gz servo-9aeb1fb475a8f03413c05b4b7608556aa781560f.zip |
auto merge of #1420 : saneyuki/servo/compositor, r=pcwalton
This change compositor code to objective, short scope, reducing closure functions. It will increase maintainability.
Related: #1351.
Diffstat (limited to 'src')
-rw-r--r-- | src/components/main/compositing/mod.rs | 49 | ||||
-rw-r--r-- | src/components/main/compositing/run.rs | 812 | ||||
-rw-r--r-- | src/components/main/compositing/run_headless.rs | 9 |
3 files changed, 536 insertions, 334 deletions
diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index a6c1a0ffbd0..d22258170c1 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -31,7 +31,6 @@ mod compositor_layer; mod run; mod run_headless; - /// The implementation of the layers-based compositor. #[deriving(Clone)] pub struct CompositorChan { @@ -153,31 +152,18 @@ pub enum CompositorMode { pub struct CompositorTask { mode: CompositorMode, - opts: Opts, - port: Port<Msg>, - constellation_chan: ConstellationChan, - profiler_chan: ProfilerChan, } impl CompositorTask { - fn new(opts: Opts, - port: Port<Msg>, - constellation_chan: ConstellationChan, - profiler_chan: ProfilerChan) - -> CompositorTask { - - let mode: CompositorMode = if opts.headless { + fn new(is_headless: bool) -> CompositorTask { + let mode: CompositorMode = if is_headless { Headless } else { Windowed(ApplicationMethods::new()) }; CompositorTask { - mode: mode, - opts: opts, - port: port, - constellation_chan: constellation_chan, - profiler_chan: profiler_chan + mode: mode } } @@ -199,25 +185,26 @@ impl CompositorTask { profiler_chan: ProfilerChan, exit_chan: Chan<()>, exit_response_from_constellation: Port<()>) { - let compositor = CompositorTask::new(opts, - port, - constellation_chan, - profiler_chan); - compositor.run(exit_chan, exit_response_from_constellation); - } - - fn run(&self, - exit_chan: Chan<()>, - exit_response_from_constellation: Port<()>) { - match self.mode { - Windowed(ref app) => run::run_compositor(self, app), - Headless => run_headless::run_compositor(self), + + let compositor = CompositorTask::new(opts.headless); + + match compositor.mode { + Windowed(ref app) => { + run::IOCompositor::create(app, + opts, + port, + constellation_chan.clone(), + profiler_chan); + } + Headless => { + run_headless::run_compositor(&constellation_chan, port); + } } // Constellation has to be shut down before the compositor goes out of // scope, as the compositor manages setup/teardown of global subsystems debug!("shutting down the constellation"); - self.constellation_chan.send(ExitMsg(exit_chan)); + constellation_chan.send(ExitMsg(exit_chan)); exit_response_from_constellation.recv(); } } diff --git a/src/components/main/compositing/run.rs b/src/components/main/compositing/run.rs index 230fadb4c79..880beb82aa6 100644 --- a/src/components/main/compositing/run.rs +++ b/src/components/main/compositing/run.rs @@ -2,33 +2,39 @@ * 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 constellation::SendableFrameTree; use compositing::compositor_layer::CompositorLayer; use compositing::*; use platform::{Application, Window}; -use windowing::{WindowEvent, WindowMethods}; -use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass}; -use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent}; -use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent}; -use windowing::RefreshWindowEvent; +use windowing::{WindowEvent, WindowMethods, + WindowNavigateMsg, + IdleWindowEvent, RefreshWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, + MouseWindowEventClass,ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, + FinishedWindowEvent, QuitWindowEvent, + MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent}; -use azure::azure_hl::SourceSurfaceMethods; + +use azure::azure_hl::{SourceSurfaceMethods, Color}; use azure::azure_hl; use extra::time::precise_time_s; use geom::matrix::identity; use geom::point::Point2D; use geom::rect::Rect; use geom::size::Size2D; +use gfx::opts::Opts; use layers::layers::{ContainerLayer, ContainerLayerKind}; +use layers::platform::surface::NativeCompositingGraphicsContext; use layers::rendergl; +use layers::rendergl::RenderContext; use layers::scene::Scene; use opengles::gl2; use png; -use servo_msg::compositor_msg::IdleRenderState; -use servo_msg::constellation_msg::{ConstellationChan, NavigateMsg, ResizedWindowMsg, LoadUrlMsg}; +use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBufferSet, RenderState}; +use servo_msg::constellation_msg::{ConstellationChan, NavigateMsg, ResizedWindowMsg, LoadUrlMsg, PipelineId}; use servo_msg::constellation_msg; -use servo_util::time::profile; +use servo_util::time::{profile, ProfilerChan}; use servo_util::{time, url}; use std::comm::Port; use std::num::Orderable; @@ -36,339 +42,582 @@ use std::path::Path; use std::rt::io::timer::Timer; use std::vec; -/// Starts the compositor, which listens for messages on the specified port. -pub fn run_compositor(compositor: &CompositorTask, app: &Application) { - let window: @mut Window = WindowMethods::new(app); - - // Create an initial layer tree. - // - // TODO: There should be no initial layer tree until the renderer creates one from the display - // list. This is only here because we don't have that logic in the renderer yet. - let context = rendergl::init_render_context(); - let root_layer = @mut ContainerLayer(); - let window_size = window.size(); - let mut scene = Scene(ContainerLayerKind(root_layer), window_size, identity()); - let mut window_size = Size2D(window_size.width as uint, window_size.height as uint); - let mut done = false; - let mut recomposite = false; - let graphics_context = CompositorTask::create_graphics_context(); - - // Tracks whether the renderer has finished its first rendering - let mut composite_ready = false; - - // Keeps track of the current zoom factor - let mut world_zoom = 1f32; - let mut zoom_action = false; - let mut zoom_time = 0f64; - - // The root CompositorLayer - let mut compositor_layer: Option<CompositorLayer> = None; - let mut constellation_chan: ConstellationChan = compositor.constellation_chan.clone(); - - // Get BufferRequests from each layer. - let ask_for_tiles = || { - let window_size_page = Size2D(window_size.width as f32 / world_zoom, - window_size.height as f32 / world_zoom); - for layer in compositor_layer.mut_iter() { - if !layer.hidden { - let rect = Rect(Point2D(0f32, 0f32), window_size_page); - recomposite = layer.get_buffer_request(&graphics_context, rect, world_zoom) || - recomposite; - } else { - debug!("Compositor: root layer is hidden!"); + +pub struct IOCompositor { + /// The application window. + window: @mut Window, + + /// The port on which we receive messages. + port: Port<Msg>, + + /// The render context. + context: RenderContext, + + /// The root ContainerLayer. + root_layer: @mut ContainerLayer, + + /// The canvas to paint a page. + scene: Scene, + + /// The application window size. + window_size: Size2D<uint>, + + /// The platform-specific graphics context. + graphics_context: NativeCompositingGraphicsContext, + + /// Tracks whether the renderer has finished its first rendering + composite_ready: bool, + + /// Tracks whether we should close compositor. + done: bool, + + /// Tracks whether we need to re-composite a page. + recomposite: bool, + + /// Keeps track of the current zoom factor. + world_zoom: f32, + + /// Tracks whether the zoom action has happend recently. + zoom_action: bool, + + /// The time of the last zoom action has started. + zoom_time: f64, + + /// The command line option flags. + opts: Opts, + + /// The root CompositorLayer + compositor_layer: Option<CompositorLayer>, + + /// The channel on which messages can be sent to the constellation. + constellation_chan: ConstellationChan, + + /// The channel on which messages can be sent to the profiler. + profiler_chan: ProfilerChan +} + +impl IOCompositor { + + pub fn new(app: &Application, + opts: Opts, + port: Port<Msg>, + constellation_chan: ConstellationChan, + profiler_chan: ProfilerChan) -> IOCompositor { + let window: @mut Window = WindowMethods::new(app); + + // Create an initial layer tree. + // + // TODO: There should be no initial layer tree until the renderer creates one from the display + // list. This is only here because we don't have that logic in the renderer yet. + let root_layer = @mut ContainerLayer(); + let window_size = window.size(); + + IOCompositor { + window: window, + port: port, + opts: opts, + context: rendergl::init_render_context(), + root_layer: root_layer, + scene: Scene(ContainerLayerKind(root_layer), window_size, identity()), + window_size: Size2D(window_size.width as uint, window_size.height as uint), + graphics_context: CompositorTask::create_graphics_context(), + composite_ready: false, + done: false, + recomposite: false, + world_zoom: 1f32, + zoom_action: false, + zoom_time: 0f64, + compositor_layer: None, + constellation_chan: constellation_chan.clone(), + profiler_chan: profiler_chan + } + } + + pub fn create(app: &Application, + opts: Opts, + port: Port<Msg>, + constellation_chan: ConstellationChan, + profiler_chan: ProfilerChan) { + let mut compositor = IOCompositor::new(app, + opts, + port, + constellation_chan, + profiler_chan); + + // Starts the compositor, which listens for messages on the specified port. + compositor.run(); + } + + fn run (&mut self) { + // Tell the constellation about the initial window size. + self.constellation_chan.send(ResizedWindowMsg(self.window_size)); + + // Enter the main event loop. + let mut tm = Timer::new().unwrap(); + while !self.done { + // Check for new messages coming from the rendering task. + self.handle_message(); + + // Check for messages coming from the windowing system. + self.handle_window_message(self.window.recv()); + + // If asked to recomposite and renderer has run at least once + if self.recomposite && self.composite_ready { + self.recomposite = false; + self.composite(); } + + tm.sleep(10); + + // If a pinch-zoom happened recently, ask for tiles at the new resolution + if self.zoom_action && precise_time_s() - self.zoom_time > 0.3 { + self.zoom_action = false; + self.ask_for_tiles(); + } + + } + + // Clear out the compositor layers so that painting tasks can destroy the buffers. + match self.compositor_layer { + None => {} + Some(ref mut layer) => layer.forget_all_tiles(), } - }; - let check_for_messages: &fn(&Port<Msg>) = |port: &Port<Msg>| { + // Drain compositor port, sometimes messages contain channels that are blocking + // another task from finishing (i.e. SetIds) + while self.port.peek() { self.port.recv(); } + } + + fn handle_message(&mut self) { // Handle messages - while port.peek() { - match port.recv() { - Exit => done = true, + while self.port.peek() { + match self.port.recv() { + Exit => { + self.done = true + }, + + ChangeReadyState(ready_state) => { + self.window.set_ready_state(ready_state); + } - ChangeReadyState(ready_state) => window.set_ready_state(ready_state), ChangeRenderState(render_state) => { - window.set_render_state(render_state); - if render_state == IdleRenderState { composite_ready = true; } + self.change_render_state(render_state); } SetUnRenderedColor(_id, color) => { - match compositor_layer { - Some(ref mut layer) => { - layer.unrendered_color = color; - } - None => {} - } + self.set_unrendered_color(_id, color); } SetIds(frame_tree, response_chan, new_constellation_chan) => { - response_chan.send(()); - - // This assumes there is at most one child, which should be the case. - match root_layer.first_child { - Some(old_layer) => root_layer.remove_child(old_layer), - None => {} - } - - let layer = CompositorLayer::from_frame_tree(frame_tree, - compositor.opts.tile_size, - Some(10000000u), - compositor.opts.cpu_painting); - root_layer.add_child_start(ContainerLayerKind(layer.root_layer)); - - // If there's already a root layer, destroy it cleanly. - match compositor_layer { - None => {} - Some(ref mut compositor_layer) => compositor_layer.clear_all(), - } - - compositor_layer = Some(layer); - - // Initialize the new constellation channel by sending it the root window size. - let window_size = window.size(); - let window_size = Size2D(window_size.width as uint, - window_size.height as uint); - new_constellation_chan.send(ResizedWindowMsg(window_size)); - - constellation_chan = new_constellation_chan; + self.set_ids(frame_tree, response_chan, new_constellation_chan); } - GetGraphicsMetadata(chan) => chan.send(Some(azure_hl::current_graphics_metadata())), + GetGraphicsMetadata(chan) => { + chan.send(Some(azure_hl::current_graphics_metadata())); + } NewLayer(_id, new_size) => { - // FIXME: This should create an additional layer instead of replacing the current one. - // Once ResizeLayer messages are set up, we can switch to the new functionality. - - let p = match compositor_layer { - Some(ref compositor_layer) => compositor_layer.pipeline.clone(), - None => fail!("Compositor: Received new layer without initialized pipeline"), - }; - let page_size = Size2D(new_size.width as f32, new_size.height as f32); - let new_layer = CompositorLayer::new(p, - Some(page_size), - compositor.opts.tile_size, - Some(10000000u), - compositor.opts.cpu_painting); - - let current_child = root_layer.first_child; - // This assumes there is at most one child, which should be the case. - match current_child { - Some(old_layer) => root_layer.remove_child(old_layer), - None => {} - } - root_layer.add_child_start(ContainerLayerKind(new_layer.root_layer)); - compositor_layer = Some(new_layer); - - ask_for_tiles(); + self.create_new_layer(_id, new_size); } SetLayerPageSize(id, new_size, epoch) => { - match compositor_layer { - Some(ref mut layer) => { - let page_window = Size2D(window_size.width as f32 / world_zoom, - window_size.height as f32 / world_zoom); - assert!(layer.resize(id, new_size, page_window, epoch)); - ask_for_tiles(); - } - None => {} - } + self.set_layer_page_size(id, new_size, epoch); } SetLayerClipRect(id, new_rect) => { - match compositor_layer { - Some(ref mut layer) => { - assert!(layer.set_clipping_rect(id, new_rect)); - ask_for_tiles(); - } - None => {} - } + self.set_layer_clip_rect(id, new_rect); } DeleteLayer(id) => { - match compositor_layer { - Some(ref mut layer) => { - assert!(layer.delete(&graphics_context, id)); - ask_for_tiles(); - } - None => {} - } + self.delete_layer(id); } Paint(id, new_layer_buffer_set, epoch) => { - debug!("osmain: received new frame"); - - // From now on, if we destroy the buffers, they will leak. - let mut new_layer_buffer_set = new_layer_buffer_set; - new_layer_buffer_set.mark_will_leak(); - - match compositor_layer { - Some(ref mut layer) => { - assert!(layer.add_buffers(&graphics_context, - id, - new_layer_buffer_set, - epoch).is_none()); - recomposite = true; - } - None => { - fail!("Compositor: given paint command with no CompositorLayer initialized"); - } - } - // TODO: Recycle the old buffers; send them back to the renderer to reuse if - // it wishes. + self.paint(id, new_layer_buffer_set, epoch); } InvalidateRect(id, rect) => { - match compositor_layer { - Some(ref mut layer) => { - layer.invalidate_rect(id, Rect(Point2D(rect.origin.x as f32, - rect.origin.y as f32), - Size2D(rect.size.width as f32, - rect.size.height as f32))); - ask_for_tiles(); - } - None => {} // Nothing to do - } + self.invalidate_rect(id, rect); } ScrollFragmentPoint(id, point) => { - let page_window = Size2D(window_size.width as f32 / world_zoom, - window_size.height as f32 / world_zoom); - match compositor_layer { - Some(ref mut layer) if layer.pipeline.id == id => { - recomposite = layer.move(point, page_window) | recomposite; - ask_for_tiles(); - } - Some(_) | None => {} - } + self.scroll_fragment_to_point(id, point); } } } - }; + } + + fn change_render_state(&mut self, render_state: RenderState) { + self.window.set_render_state(render_state); + if render_state == IdleRenderState { + self.composite_ready = true; + } + } + + fn set_unrendered_color(&mut self, _id: PipelineId, color: Color) { + match self.compositor_layer { + Some(ref mut layer) => { + layer.unrendered_color = color; + } + None => {} + } + } + + fn set_ids(&mut self, + frame_tree: SendableFrameTree, + response_chan: Chan<()>, + new_constellation_chan: ConstellationChan) { + response_chan.send(()); + + // This assumes there is at most one child, which should be the case. + match self.root_layer.first_child { + Some(old_layer) => self.root_layer.remove_child(old_layer), + None => {} + } + + let layer = CompositorLayer::from_frame_tree(frame_tree, + self.opts.tile_size, + Some(10000000u), + self.opts.cpu_painting); + self.root_layer.add_child_start(ContainerLayerKind(layer.root_layer)); + + // If there's already a root layer, destroy it cleanly. + match self.compositor_layer { + None => {} + Some(ref mut compositor_layer) => compositor_layer.clear_all(), + } + + self.compositor_layer = Some(layer); + + // 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); + new_constellation_chan.send(ResizedWindowMsg(window_size)); + + self.constellation_chan = new_constellation_chan; + } + + fn create_new_layer(&mut self, _id: PipelineId, new_size: Size2D<f32>) { + // FIXME: This should create an additional layer instead of replacing the current one. + // Once ResizeLayer messages are set up, we can switch to the new functionality. + + let p = match self.compositor_layer { + Some(ref compositor_layer) => compositor_layer.pipeline.clone(), + None => fail!("Compositor: Received new layer without initialized pipeline"), + }; + let page_size = Size2D(new_size.width as f32, new_size.height as f32); + let new_layer = CompositorLayer::new(p, + Some(page_size), + self.opts.tile_size, + Some(10000000u), + self.opts.cpu_painting); + + let current_child = self.root_layer.first_child; + // This assumes there is at most one child, which should be the case. + match current_child { + Some(old_layer) => self.root_layer.remove_child(old_layer), + None => {} + } + self.root_layer.add_child_start(ContainerLayerKind(new_layer.root_layer)); + self.compositor_layer = Some(new_layer); + + self.ask_for_tiles(); + } + + fn set_layer_page_size(&mut self, + id: PipelineId, + new_size: Size2D<f32>, + epoch: Epoch) { + let ask: 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); + assert!(layer.resize(id, new_size, page_window, epoch)); + true + } + None => { + false + } + }; + + if ask { + self.ask_for_tiles(); + } + } + + fn set_layer_clip_rect(&mut self, id: PipelineId, new_rect: Rect<f32>) { + let ask: bool = match self.compositor_layer { + Some(ref mut layer) => { + assert!(layer.set_clipping_rect(id, new_rect)); + true + } + None => { + false + } + }; + + if ask { + self.ask_for_tiles(); + } + } + + fn delete_layer(&mut self, id: PipelineId) { + let ask: bool = match self.compositor_layer { + Some(ref mut layer) => { + assert!(layer.delete(&self.graphics_context, id)); + true + } + None => { + false + } + }; + + if ask { + self.ask_for_tiles(); + } + } - let check_for_window_messages: &fn(WindowEvent) = |event| { + fn paint(&mut self, + id: PipelineId, + new_layer_buffer_set: ~LayerBufferSet, + epoch: Epoch) { + debug!("osmain: received new frame"); + + // From now on, if we destroy the buffers, they will leak. + let mut new_layer_buffer_set = new_layer_buffer_set; + new_layer_buffer_set.mark_will_leak(); + + match self.compositor_layer { + Some(ref mut layer) => { + assert!(layer.add_buffers(&self.graphics_context, + id, + new_layer_buffer_set, + epoch).is_none()); + self.recomposite = true; + } + None => { + fail!("Compositor: given paint command with no CompositorLayer initialized"); + } + } + // TODO: Recycle the old buffers; send them back to the renderer to reuse if + // it wishes. + } + + fn invalidate_rect(&mut self, id: PipelineId, rect: Rect<uint>) { + let ask: bool = match self.compositor_layer { + Some(ref mut layer) => { + let point = Point2D(rect.origin.x as f32, + rect.origin.y as f32); + let size = Size2D(rect.size.width as f32, + rect.size.height as f32); + layer.invalidate_rect(id, Rect(point, size)); + true + } + None => { + // Nothing to do + false + } + }; + + if ask { + self.ask_for_tiles(); + } + } + + fn scroll_fragment_to_point(&mut self, id: PipelineId, point: Point2D<f32>) { + 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 ask: bool = match self.compositor_layer { + Some(ref mut layer) if layer.pipeline.id == id => { + let recomposite = layer.move(point, page_window) | self.recomposite; + self.recomposite = recomposite; + + true + } + Some(_) | None => { + false + } + }; + + if ask { + self.ask_for_tiles(); + } + } + + fn handle_window_message(&mut self, event: WindowEvent) { match event { IdleWindowEvent => {} RefreshWindowEvent => { - recomposite = true; + self.recomposite = true; } ResizeWindowEvent(width, height) => { - let new_size = Size2D(width, height); - if window_size != new_size { - debug!("osmain: window resized to {:u}x{:u}", width, height); - window_size = new_size; - constellation_chan.send(ResizedWindowMsg(new_size)) - } else { - debug!("osmain: dropping window resize since size is still {:u}x{:u}", width, height); - } + self.on_resize_window_event(width, height); } LoadUrlWindowEvent(url_string) => { - debug!("osmain: loading URL `{:s}`", url_string); - let root_pipeline_id = match compositor_layer { - Some(ref layer) => layer.pipeline.id.clone(), - None => fail!("Compositor: Received LoadUrlWindowEvent without initialized compositor layers"), - }; - constellation_chan.send(LoadUrlMsg(root_pipeline_id, - url::make_url(url_string.to_str(), None))) + self.on_load_url_window_event(url_string); } MouseWindowEventClass(mouse_window_event) => { - 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), - }; - for layer in compositor_layer.iter() { - layer.send_mouse_event(mouse_window_event, point); - } + self.on_mouse_window_event_class(mouse_window_event); } ScrollWindowEvent(delta, cursor) => { - // 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<f32> = Point2D(cursor.x as f32 / world_zoom, - cursor.y as f32 / world_zoom); - let page_window = Size2D(window_size.width as f32 / world_zoom, - window_size.height as f32 / world_zoom); - for layer in compositor_layer.mut_iter() { - recomposite = layer.scroll(page_delta, page_cursor, page_window) || recomposite; - } - ask_for_tiles(); + self.on_scroll_window_event(delta, cursor); } ZoomWindowEvent(magnification) => { - zoom_action = true; - zoom_time = precise_time_s(); - let old_world_zoom = world_zoom; - - // Determine zoom amount - world_zoom = (world_zoom * magnification).max(&1.0); - root_layer.common.set_transform(identity().scale(world_zoom, world_zoom, 1f32)); - - // 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); - // 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); - for layer in compositor_layer.mut_iter() { - layer.scroll(page_delta, page_cursor, page_window); - } - - recomposite = true; + self.on_zoom_window_event(magnification); } NavigationWindowEvent(direction) => { - let direction = match direction { - windowing::Forward => constellation_msg::Forward, - windowing::Back => constellation_msg::Back, - }; - constellation_chan.send(NavigateMsg(direction)) + self.on_navigation_window_event(direction); } FinishedWindowEvent => { - if compositor.opts.exit_after_load { - done = true; + let exit = self.opts.exit_after_load; + if exit { + self.done = true; } } QuitWindowEvent => { - done = true; + self.done = true; } } - }; + } + + fn on_resize_window_event(&mut self, width: uint, height: uint) { + let new_size = Size2D(width, height); + if self.window_size != new_size { + debug!("osmain: window resized to {:u}x{:u}", width, height); + self.window_size = new_size; + self.constellation_chan.send(ResizedWindowMsg(new_size)) + } else { + debug!("osmain: dropping window resize since size is still {:u}x{:u}", width, height); + } + } + + fn on_load_url_window_event(&self, url_string: ~str) { + debug!("osmain: loading URL `{:s}`", url_string); + let root_pipeline_id = match self.compositor_layer { + Some(ref layer) => layer.pipeline.id.clone(), + None => fail!("Compositor: Received LoadUrlWindowEvent without initialized compositor layers"), + }; + let msg = LoadUrlMsg(root_pipeline_id, url::make_url(url_string.to_str(), None)); + self.constellation_chan.send(msg); + } - let profiler_chan = compositor.profiler_chan.clone(); - let write_png = compositor.opts.output_file.is_some(); - let exit = compositor.opts.exit_after_load; - let composite = || { - do profile(time::CompositingCategory, profiler_chan.clone()) { + fn on_mouse_window_event_class(&self, mouse_window_event: MouseWindowEvent) { + let world_zoom = self.world_zoom; + 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), + }; + for layer in self.compositor_layer.iter() { + layer.send_mouse_event(mouse_window_event, point); + } + } + + fn on_scroll_window_event(&mut self, delta: Point2D<f32>, cursor: Point2D<i32>) { + let world_zoom = self.world_zoom; + // 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<f32> = 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); + for layer in self.compositor_layer.mut_iter() { + let recomposite = layer.scroll(page_delta, page_cursor, page_window) || self.recomposite; + self.recomposite = recomposite; + } + self.ask_for_tiles(); + } + + 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; + + // Determine zoom amount + self.world_zoom = (self.world_zoom * magnification).max(&1.0); + let world_zoom = self.world_zoom; + + self.root_layer.common.set_transform(identity().scale(world_zoom, world_zoom, 1f32)); + + // 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); + // 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); + for layer in self.compositor_layer.mut_iter() { + layer.scroll(page_delta, page_cursor, page_window); + } + + self.recomposite = true; + } + + fn on_navigation_window_event(&self, direction: WindowNavigateMsg) { + let direction = match direction { + windowing::Forward => constellation_msg::Forward, + windowing::Back => constellation_msg::Back, + }; + self.constellation_chan.send(NavigateMsg(direction)) + } + + /// 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); + for layer in self.compositor_layer.mut_iter() { + if !layer.hidden { + let rect = Rect(Point2D(0f32, 0f32), window_size_page); + let recomposite = layer.get_buffer_request(&self.graphics_context, rect, world_zoom) || + self.recomposite; + self.recomposite = recomposite; + } else { + debug!("Compositor: root layer is hidden!"); + } + } + } + + fn composite(&mut self) { + do profile(time::CompositingCategory, self.profiler_chan.clone()) { debug!("compositor: compositing"); // Adjust the layer dimensions as necessary to correspond to the size of the window. - scene.size = window.size(); + self.scene.size = self.window.size(); // Render the scene. - match compositor_layer { + match self.compositor_layer { Some(ref mut layer) => { - scene.background_color.r = layer.unrendered_color.r; - scene.background_color.g = layer.unrendered_color.g; - scene.background_color.b = layer.unrendered_color.b; - scene.background_color.a = layer.unrendered_color.a; + self.scene.background_color.r = layer.unrendered_color.r; + self.scene.background_color.g = layer.unrendered_color.g; + self.scene.background_color.b = layer.unrendered_color.b; + self.scene.background_color.a = layer.unrendered_color.a; } None => {} } - rendergl::render_scene(context, &scene); + rendergl::render_scene(self.context, &self.scene); } // Render to PNG. We must read from the back buffer (ie, before - // window.present()) as OpenGL ES 2 does not have glReadBuffer(). + // self.window.present()) as OpenGL ES 2 does not have glReadBuffer(). + let write_png = self.opts.output_file.is_some(); if write_png { - let (width, height) = (window_size.width as uint, window_size.height as uint); - let path = from_str::<Path>(*compositor.opts.output_file.get_ref()).unwrap(); + let (width, height) = (self.window_size.width as uint, self.window_size.height as uint); + let path = from_str::<Path>(*self.opts.output_file.get_ref()).unwrap(); let mut pixels = gl2::read_pixels(0, 0, width as gl2::GLsizei, height as gl2::GLsizei, @@ -392,49 +641,14 @@ pub fn run_compositor(compositor: &CompositorTask, app: &Application) { let res = png::store_png(&img, &path); assert!(res.is_ok()); - done = true; - } - - window.present(); - - if exit { done = true; } - }; - - // Tell the constellation about the initial window size. - constellation_chan.send(ResizedWindowMsg(window_size)); - - // Enter the main event loop. - let mut tm = Timer::new().unwrap(); - while !done { - // Check for new messages coming from the rendering task. - check_for_messages(&compositor.port); - - // Check for messages coming from the windowing system. - check_for_window_messages(window.recv()); - - // If asked to recomposite and renderer has run at least once - if recomposite && composite_ready { - recomposite = false; - composite(); + self.done = true; } - tm.sleep(10); + self.window.present(); - // If a pinch-zoom happened recently, ask for tiles at the new resolution - if zoom_action && precise_time_s() - zoom_time > 0.3 { - zoom_action = false; - ask_for_tiles(); + let exit = self.opts.exit_after_load; + if exit { + self.done = true; } - - } - - // Clear out the compositor layers so that painting tasks can destroy the buffers. - match compositor_layer { - None => {} - Some(ref mut layer) => layer.forget_all_tiles(), } - - // Drain compositor port, sometimes messages contain channels that are blocking - // another task from finishing (i.e. SetIds) - while compositor.port.peek() { compositor.port.recv(); } } diff --git a/src/components/main/compositing/run_headless.rs b/src/components/main/compositing/run_headless.rs index ed5f23d3e2e..0d663aeb580 100644 --- a/src/components/main/compositing/run_headless.rs +++ b/src/components/main/compositing/run_headless.rs @@ -5,18 +5,19 @@ use compositing::*; use geom::size::Size2D; -use servo_msg::constellation_msg::ResizedWindowMsg; +use servo_msg::constellation_msg::{ConstellationChan, ResizedWindowMsg}; +use std::comm::Port; /// Starts the compositor, which listens for messages on the specified port. /// /// This is the null compositor which doesn't draw anything to the screen. /// It's intended for headless testing. -pub fn run_compositor(compositor: &CompositorTask) { +pub fn run_compositor(constellation_chan: &ConstellationChan, port: Port<Msg>) { // Tell the constellation about the initial fake size. - compositor.constellation_chan.send(ResizedWindowMsg(Size2D(640u, 480u))); + constellation_chan.send(ResizedWindowMsg(Size2D(640u, 480u))); loop { - match compositor.port.recv() { + match port.recv() { Exit => break, GetGraphicsMetadata(chan) => { |