aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKeegan McAllister <kmcallister@mozilla.com>2013-10-18 15:46:47 -0700
committerKeegan McAllister <kmcallister@mozilla.com>2013-10-22 13:35:09 -0700
commitecc3db7b1f9ea5088b39684c687985667b83bd3d (patch)
tree60602d2190a89f4a3135d0e04848f5e3241b3eb7 /src
parent797143a1d3e09b4ddd35a1ce5948be424ec3f9ef (diff)
downloadservo-ecc3db7b1f9ea5088b39684c687985667b83bd3d.tar.gz
servo-ecc3db7b1f9ea5088b39684c687985667b83bd3d.zip
Split out the part of the compositor which makes OpenGL calls
Diffstat (limited to 'src')
-rw-r--r--src/components/main/compositing/mod.rs391
-rw-r--r--src/components/main/compositing/run.rs405
2 files changed, 408 insertions, 388 deletions
diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs
index 7e236dddc70..1575e60f975 100644
--- a/src/components/main/compositing/mod.rs
+++ b/src/components/main/compositing/mod.rs
@@ -2,50 +2,27 @@
* 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 platform::{Application, Window};
-
pub use windowing;
-use windowing::{ApplicationMethods, WindowEvent, WindowMethods};
-use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass};
-use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent};
-use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
use servo_msg::compositor_msg::{RenderListener, LayerBufferSet, RenderState};
use servo_msg::compositor_msg::{ReadyState, ScriptListener, Epoch};
-use servo_msg::constellation_msg::{ConstellationChan, NavigateMsg, PipelineId, ResizedWindowMsg, LoadUrlMsg};
-use servo_msg::constellation_msg;
+use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
use gfx::opts::Opts;
-use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context};
use azure::azure::AzGLContext;
use std::comm;
use std::comm::{Chan, SharedChan, Port};
use std::num::Orderable;
-use std::vec;
-use std::path::Path;
-use std::rt::io::timer::Timer;
-use geom::matrix::identity;
use geom::point::Point2D;
use geom::size::Size2D;
use geom::rect::Rect;
-use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format};
-use layers::layers::{ImageData, WithDataFn};
-use layers::rendergl;
-use layers::scene::Scene;
-use opengles::gl2;
-use png;
-use servo_util::{time, url};
-use servo_util::time::profile;
use servo_util::time::ProfilerChan;
-use extra::future::Future;
-use extra::time::precise_time_s;
-
use constellation::SendableFrameTree;
-use compositing::compositor_layer::CompositorLayer;
mod quadtree;
mod compositor_layer;
+mod run;
/// The implementation of the layers-based compositor.
@@ -160,31 +137,6 @@ pub enum Msg {
SetIds(SendableFrameTree, Chan<()>, ConstellationChan),
}
-/// Azure surface wrapping to work with the layers infrastructure.
-struct AzureDrawTargetImageData {
- draw_target: DrawTarget,
- data_source_surface: DataSourceSurface,
- size: Size2D<uint>,
-}
-
-impl ImageData for AzureDrawTargetImageData {
- fn size(&self) -> Size2D<uint> {
- self.size
- }
- fn stride(&self) -> uint {
- self.data_source_surface.stride() as uint
- }
- fn format(&self) -> Format {
- // FIXME: This is not always correct. We should query the Azure draw target for the format.
- ARGB32Format
- }
- fn with_data(&self, f: WithDataFn) {
- do self.data_source_surface.with_data |data| {
- f(data);
- }
- }
-}
-
pub struct CompositorTask {
opts: Opts,
port: Port<Msg>,
@@ -206,344 +158,7 @@ impl CompositorTask {
}
}
- /// Starts the compositor, which listens for messages on the specified port.
pub fn run(&self) {
- let app: Application = ApplicationMethods::new();
- 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;
-
- // 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: Option<ConstellationChan> = None;
-
- // 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 {
- recomposite = layer.get_buffer_request(Rect(Point2D(0f32, 0f32), window_size_page),
- world_zoom) || recomposite;
- } else {
- debug!("Compositor: root layer is hidden!");
- }
- }
- };
-
- let check_for_messages: &fn(&Port<Msg>) = |port: &Port<Msg>| {
- // Handle messages
- while port.peek() {
- match port.recv() {
- Exit => done = true,
-
- ChangeReadyState(ready_state) => window.set_ready_state(ready_state),
- ChangeRenderState(render_state) => window.set_render_state(render_state),
-
- 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,
- self.opts.tile_size,
- Some(10000000u));
- root_layer.add_child_start(ContainerLayerKind(layer.root_layer));
- compositor_layer = Some(layer);
-
- constellation_chan = Some(new_constellation_chan);
- }
-
- GetSize(chan) => {
- let size = window.size();
- chan.send(Size2D(size.width as int, size.height as int));
- }
-
- GetGLContext(chan) => chan.send(current_gl_context()),
-
- 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),
- self.opts.tile_size, Some(10000000u));
-
- 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();
- }
-
- 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 => {}
- }
- }
-
- SetLayerClipRect(id, new_rect) => {
- match compositor_layer {
- Some(ref mut layer) => {
- assert!(layer.set_clipping_rect(id, new_rect));
- ask_for_tiles();
- }
- None => {}
- }
- }
-
- DeleteLayer(id) => {
- match compositor_layer {
- Some(ref mut layer) => {
- assert!(layer.delete(id));
- ask_for_tiles();
- }
- None => {}
- }
- }
-
- Paint(id, new_layer_buffer_set, epoch) => {
- debug!("osmain: received new frame");
-
- match compositor_layer {
- Some(ref mut layer) => {
- assert!(layer.add_buffers(id, new_layer_buffer_set, epoch));
- 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.
- }
-
- 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
- }
- }
- }
- }
- };
-
- let check_for_window_messages: &fn(WindowEvent) = |event| {
- match event {
- IdleWindowEvent => {}
-
- ResizeWindowEvent(width, height) => {
- let new_size = Size2D(width, height);
- if window_size != new_size {
- debug!("osmain: window resized to %ux%u", width, height);
- window_size = new_size;
- match constellation_chan {
- Some(ref chan) => chan.send(ResizedWindowMsg(new_size)),
- None => error!("Compositor: Recieved resize event without initialized layout chan"),
- }
- } else {
- debug!("osmain: dropping window resize since size is still %ux%u", 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"),
- };
- match constellation_chan {
- Some(ref chan) => chan.send(LoadUrlMsg(root_pipeline_id,
- url::make_url(url_string.to_str(), None),
- Future::from_value(window_size))),
- None => error!("Compositor: Recieved loadurl event without initialized layout chan"),
- }
- }
-
- 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);
- }
- }
-
- 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();
- }
-
- 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;
- }
-
- NavigationWindowEvent(direction) => {
- let direction = match direction {
- windowing::Forward => constellation_msg::Forward,
- windowing::Back => constellation_msg::Back,
- };
- match constellation_chan {
- Some(ref chan) => chan.send(NavigateMsg(direction)),
- None => error!("Compositor: Recieved navigation event without initialized layout chan"),
- }
- }
-
- FinishedWindowEvent => {
- if self.opts.exit_after_load {
- done = true;
- }
- }
-
- QuitWindowEvent => {
- done = true;
- }
- }
- };
-
-
- let profiler_chan = self.profiler_chan.clone();
- let write_png = self.opts.output_file.is_some();
- let exit = self.opts.exit_after_load;
- let composite = || {
- do profile(time::CompositingCategory, profiler_chan.clone()) {
- debug!("compositor: compositing");
- // Adjust the layer dimensions as necessary to correspond to the size of the window.
- scene.size = window.size();
-
- // Render the scene.
- rendergl::render_scene(context, &scene);
- }
-
- // Render to PNG. We must read from the back buffer (ie, before
- // window.present()) as OpenGL ES 2 does not have glReadBuffer().
- if write_png {
- let (width, height) = (window_size.width as uint, 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,
- gl2::RGB, gl2::UNSIGNED_BYTE);
- // flip image vertically (texture is upside down)
- let orig_pixels = pixels.clone();
- let stride = width * 3;
- for y in range(0, height) {
- let dst_start = y * stride;
- let src_start = (height - y - 1) * stride;
- vec::bytes::copy_memory(pixels.mut_slice(dst_start, dst_start + stride),
- orig_pixels.slice(src_start, src_start + stride),
- stride);
- }
- let img = png::Image {
- width: width as u32,
- height: height as u32,
- color_type: png::RGB8,
- pixels: pixels,
- };
- let res = png::store_png(&img, &path);
- assert!(res.is_ok());
-
- done = true;
- }
-
- window.present();
-
- if exit { done = true; }
- };
-
- // 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(&self.port);
-
- // Check for messages coming from the windowing system.
- check_for_window_messages(window.recv());
-
- if recomposite {
- recomposite = false;
- composite();
- }
-
- tm.sleep(10);
-
- // 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();
- }
-
- }
-
- self.shutdown_chan.send(())
+ run::run_compositor(self);
}
}
diff --git a/src/components/main/compositing/run.rs b/src/components/main/compositing/run.rs
new file mode 100644
index 00000000000..7fda95b3d83
--- /dev/null
+++ b/src/components/main/compositing/run.rs
@@ -0,0 +1,405 @@
+/* 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 platform::{Application, Window};
+
+use windowing::{ApplicationMethods, WindowEvent, WindowMethods};
+use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass};
+use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent};
+use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
+
+use servo_msg::constellation_msg::{ConstellationChan, NavigateMsg, ResizedWindowMsg, LoadUrlMsg};
+use servo_msg::constellation_msg;
+
+use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context};
+use std::comm::Port;
+use std::num::Orderable;
+use std::vec;
+use std::path::Path;
+use std::rt::io::timer::Timer;
+use geom::matrix::identity;
+use geom::point::Point2D;
+use geom::size::Size2D;
+use geom::rect::Rect;
+use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format};
+use layers::layers::{ImageData, WithDataFn};
+use layers::rendergl;
+use layers::scene::Scene;
+use opengles::gl2;
+use png;
+use servo_util::{time, url};
+use servo_util::time::profile;
+
+use extra::future::Future;
+use extra::time::precise_time_s;
+
+use compositing::compositor_layer::CompositorLayer;
+
+use compositing::*;
+
+/// Azure surface wrapping to work with the layers infrastructure.
+struct AzureDrawTargetImageData {
+ draw_target: DrawTarget,
+ data_source_surface: DataSourceSurface,
+ size: Size2D<uint>,
+}
+
+impl ImageData for AzureDrawTargetImageData {
+ fn size(&self) -> Size2D<uint> {
+ self.size
+ }
+ fn stride(&self) -> uint {
+ self.data_source_surface.stride() as uint
+ }
+ fn format(&self) -> Format {
+ // FIXME: This is not always correct. We should query the Azure draw target for the format.
+ ARGB32Format
+ }
+ fn with_data(&self, f: WithDataFn) {
+ do self.data_source_surface.with_data |data| {
+ f(data);
+ }
+ }
+}
+
+/// Starts the compositor, which listens for messages on the specified port.
+pub fn run_compositor(compositor: &CompositorTask) {
+ let app: Application = ApplicationMethods::new();
+ 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;
+
+ // 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: Option<ConstellationChan> = None;
+
+ // 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 {
+ recomposite = layer.get_buffer_request(Rect(Point2D(0f32, 0f32), window_size_page),
+ world_zoom) || recomposite;
+ } else {
+ debug!("Compositor: root layer is hidden!");
+ }
+ }
+ };
+
+ let check_for_messages: &fn(&Port<Msg>) = |port: &Port<Msg>| {
+ // Handle messages
+ while port.peek() {
+ match port.recv() {
+ Exit => done = true,
+
+ ChangeReadyState(ready_state) => window.set_ready_state(ready_state),
+ ChangeRenderState(render_state) => window.set_render_state(render_state),
+
+ 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));
+ root_layer.add_child_start(ContainerLayerKind(layer.root_layer));
+ compositor_layer = Some(layer);
+
+ constellation_chan = Some(new_constellation_chan);
+ }
+
+ GetSize(chan) => {
+ let size = window.size();
+ chan.send(Size2D(size.width as int, size.height as int));
+ }
+
+ GetGLContext(chan) => chan.send(current_gl_context()),
+
+ 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));
+
+ 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();
+ }
+
+ 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 => {}
+ }
+ }
+
+ SetLayerClipRect(id, new_rect) => {
+ match compositor_layer {
+ Some(ref mut layer) => {
+ assert!(layer.set_clipping_rect(id, new_rect));
+ ask_for_tiles();
+ }
+ None => {}
+ }
+ }
+
+ DeleteLayer(id) => {
+ match compositor_layer {
+ Some(ref mut layer) => {
+ assert!(layer.delete(id));
+ ask_for_tiles();
+ }
+ None => {}
+ }
+ }
+
+ Paint(id, new_layer_buffer_set, epoch) => {
+ debug!("osmain: received new frame");
+
+ match compositor_layer {
+ Some(ref mut layer) => {
+ assert!(layer.add_buffers(id, new_layer_buffer_set, epoch));
+ 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.
+ }
+
+ 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
+ }
+ }
+ }
+ }
+ };
+
+ let check_for_window_messages: &fn(WindowEvent) = |event| {
+ match event {
+ IdleWindowEvent => {}
+
+ ResizeWindowEvent(width, height) => {
+ let new_size = Size2D(width, height);
+ if window_size != new_size {
+ debug!("osmain: window resized to %ux%u", width, height);
+ window_size = new_size;
+ match constellation_chan {
+ Some(ref chan) => chan.send(ResizedWindowMsg(new_size)),
+ None => error!("Compositor: Recieved resize event without initialized layout chan"),
+ }
+ } else {
+ debug!("osmain: dropping window resize since size is still %ux%u", 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"),
+ };
+ match constellation_chan {
+ Some(ref chan) => chan.send(LoadUrlMsg(root_pipeline_id,
+ url::make_url(url_string.to_str(), None),
+ Future::from_value(window_size))),
+ None => error!("Compositor: Recieved loadurl event without initialized layout chan"),
+ }
+ }
+
+ 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);
+ }
+ }
+
+ 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();
+ }
+
+ 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;
+ }
+
+ NavigationWindowEvent(direction) => {
+ let direction = match direction {
+ windowing::Forward => constellation_msg::Forward,
+ windowing::Back => constellation_msg::Back,
+ };
+ match constellation_chan {
+ Some(ref chan) => chan.send(NavigateMsg(direction)),
+ None => error!("Compositor: Recieved navigation event without initialized layout chan"),
+ }
+ }
+
+ FinishedWindowEvent => {
+ if compositor.opts.exit_after_load {
+ done = true;
+ }
+ }
+
+ QuitWindowEvent => {
+ done = true;
+ }
+ }
+ };
+
+
+ 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()) {
+ debug!("compositor: compositing");
+ // Adjust the layer dimensions as necessary to correspond to the size of the window.
+ scene.size = window.size();
+
+ // Render the scene.
+ rendergl::render_scene(context, &scene);
+ }
+
+ // Render to PNG. We must read from the back buffer (ie, before
+ // window.present()) as OpenGL ES 2 does not have glReadBuffer().
+ 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 mut pixels = gl2::read_pixels(0, 0,
+ width as gl2::GLsizei,
+ height as gl2::GLsizei,
+ gl2::RGB, gl2::UNSIGNED_BYTE);
+ // flip image vertically (texture is upside down)
+ let orig_pixels = pixels.clone();
+ let stride = width * 3;
+ for y in range(0, height) {
+ let dst_start = y * stride;
+ let src_start = (height - y - 1) * stride;
+ vec::bytes::copy_memory(pixels.mut_slice(dst_start, dst_start + stride),
+ orig_pixels.slice(src_start, src_start + stride),
+ stride);
+ }
+ let img = png::Image {
+ width: width as u32,
+ height: height as u32,
+ color_type: png::RGB8,
+ pixels: pixels,
+ };
+ let res = png::store_png(&img, &path);
+ assert!(res.is_ok());
+
+ done = true;
+ }
+
+ window.present();
+
+ if exit { done = true; }
+ };
+
+ // 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 recomposite {
+ recomposite = false;
+ composite();
+ }
+
+ tm.sleep(10);
+
+ // 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();
+ }
+
+ }
+
+ compositor.shutdown_chan.send(())
+}