diff --git a/.gitmodules b/.gitmodules index e57d923cc2a..38368bc25bf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -85,3 +85,6 @@ [submodule "src/compiler/rust"] path = src/compiler/rust url = https://github.com/mozilla/rust.git +[submodule "src/support/alert/rust-alert"] + path = src/support/alert/rust-alert + url = git://github.com/mozilla-servo/rust-alert.git diff --git a/configure b/configure index 53a0f1eb5a3..27edb45ae41 100755 --- a/configure +++ b/configure @@ -398,6 +398,7 @@ AUTOCMD="${LIBTOOLIZE} && autoconf && autoheader && automake --add-missing --cop # Copied from cairo's autogen.sh. Build fails without CFG_SUBMODULES="\ + support/alert/rust-alert \ support/azure/rust-azure \ support/css/rust-css \ support/geom/rust-geom \ diff --git a/mk/sub.mk b/mk/sub.mk index 88b9acdc8ca..e8bf4d7c67a 100644 --- a/mk/sub.mk +++ b/mk/sub.mk @@ -103,6 +103,11 @@ DEPS_rust-io-surface += \ rust-core-foundation \ $(NULL) +DEPS_rust-alert += \ + rust-core-foundation \ + rust-cocoa \ + $(NULL) + DEPS_sharegl += \ rust-core-foundation \ rust-io-surface \ diff --git a/src/components/servo/compositing/mod.rs b/src/components/servo/compositing/mod.rs index 602b1e501d5..dec19f9313d 100644 --- a/src/components/servo/compositing/mod.rs +++ b/src/components/servo/compositing/mod.rs @@ -17,8 +17,10 @@ use geom::rect::Rect; use geom::size::Size2D; use gfx::compositor::{Compositor, LayerBuffer, LayerBufferSet}; use gfx::opts::Opts; -use layers::layers::{Image, ImageData}; -use layers; +use layers::layers::{ARGB32Format, BasicImageData, ContainerLayer, ContainerLayerKind, Format}; +use layers::layers::{Image, ImageData, ImageLayer, ImageLayerKind, RGB24Format, WithDataFn}; +use layers::rendergl; +use layers::scene::Scene; use servo_util::{time, url}; mod resize_rate_limiter; @@ -35,7 +37,7 @@ impl CompositorImpl { let script_chan = Cell(script_chan); let chan: Chan = do on_osmain |port| { debug!("preparing to enter main loop"); - mainloop(port, script_chan.take(), &opts); + run_main_loop(port, script_chan.take(), &opts); }; CompositorImpl { @@ -59,59 +61,56 @@ struct AzureDrawTargetImageData { size: Size2D } -impl layers::layers::ImageData for AzureDrawTargetImageData { +impl ImageData for AzureDrawTargetImageData { fn size(&self) -> Size2D { self.size } fn stride(&self) -> uint { self.data_source_surface.stride() as uint } - fn format(&self) -> layers::layers::Format { + fn format(&self) -> Format { // FIXME: This is not always correct. We should query the Azure draw target for the format. - layers::layers::ARGB32Format + ARGB32Format } - fn with_data(&self, f: layers::layers::WithDataFn) { + fn with_data(&self, f: WithDataFn) { do self.data_source_surface.with_data |data| { f(data); } } } -fn mainloop(po: Port, script_chan: SharedChan, opts: &Opts) { - let key_handlers: @mut ~[Chan<()>] = @mut ~[]; - +fn run_main_loop(po: Port, script_chan: SharedChan, opts: &Opts) { let app: Application = ApplicationMethods::new(); let window: @mut Window = WindowMethods::new(&app); + let resize_rate_limiter = @mut ResizeRateLimiter(script_chan.clone()); - let surfaces = @mut SurfaceSet(opts.render_backend); - - let context = layers::rendergl::init_render_context(); + let surfaces = @mut SurfaceSet::new(opts.render_backend); + let context = rendergl::init_render_context(); // 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 layers::layers::ContainerLayer(); + let root_layer = @mut ContainerLayer(); let original_layer_transform; { - let image_data = @layers::layers::BasicImageData::new(Size2D(0u, 0u), - 0, - layers::layers::RGB24Format, - ~[]); + let image_data = @BasicImageData::new(Size2D(0, 0), 0, RGB24Format, ~[]); let image = @mut Image::new(image_data as @ImageData); - let image_layer = @mut layers::layers::ImageLayer(image); + let image_layer = @mut ImageLayer(image); original_layer_transform = image_layer.common.transform; image_layer.common.set_transform(original_layer_transform.scale(800.0, 600.0, 1.0)); - root_layer.add_child(layers::layers::ImageLayerKind(image_layer)); + root_layer.add_child(ImageLayerKind(image_layer)); } - - let scene = @mut layers::scene::Scene(layers::layers::ContainerLayerKind(root_layer), - Size2D(800.0, 600.0), - identity()); - + let scene = @mut Scene(ContainerLayerKind(root_layer), Size2D(800.0, 600.0), identity()); + let key_handlers: @mut ~[Chan<()>] = @mut ~[]; let done = @mut false; - let resize_rate_limiter = @mut ResizeRateLimiter(script_chan.clone()); + + // FIXME: This should not be a separate offset applied after the fact but rather should be + // applied to the layers themselves on a per-layer basis. However, this won't work until scroll + // positions are sent to content. + let world_offset = @mut Point2D(0f32, 0f32); + let check_for_messages: @fn() = || { // Periodically check if the script task responded to our last resize event resize_rate_limiter.check_resize_response(); @@ -120,11 +119,15 @@ fn mainloop(po: Port, script_chan: SharedChan, opts: &Opts) { while po.peek() { match po.recv() { AddKeyHandler(key_ch) => key_handlers.push(key_ch), - BeginDrawing(sender) => lend_surface(surfaces, sender), + BeginDrawing(sender) => surfaces.lend(sender), + Exit => *done = true, + Draw(sender, draw_target) => { debug!("osmain: received new frame"); - return_surface(surfaces, draw_target); - lend_surface(surfaces, sender); + + // Perform a buffer swap. + surfaces.put_back(draw_target); + surfaces.lend(sender); // Iterate over the children of the container layer. let mut current_layer_child = root_layer.first_child; @@ -149,11 +152,11 @@ fn mainloop(po: Port, script_chan: SharedChan, opts: &Opts) { current_layer_child = match current_layer_child { None => { debug!("osmain: adding new image layer"); - image_layer = @mut layers::layers::ImageLayer(image); - root_layer.add_child(layers::layers::ImageLayerKind(image_layer)); + image_layer = @mut ImageLayer(image); + root_layer.add_child(ImageLayerKind(image_layer)); None } - Some(layers::layers::ImageLayerKind(existing_image_layer)) => { + Some(ImageLayerKind(existing_image_layer)) => { image_layer = existing_image_layer; image_layer.set_image(image); @@ -166,16 +169,16 @@ fn mainloop(po: Port, script_chan: SharedChan, opts: &Opts) { }; // Set the layer's transform. - let x = buffer.rect.origin.x as f32; - let y = buffer.rect.origin.y as f32; - image_layer.common.set_transform( - original_layer_transform.translate(x, y, 0.0) - .scale(width as f32, height as f32, 1.0)); + let mut origin = Point2D(buffer.rect.origin.x as f32, + buffer.rect.origin.y as f32); + let transform = original_layer_transform.translate(origin.x, + origin.y, + 0.0); + let transform = transform.scale(width as f32, height as f32, 1.0); + image_layer.common.set_transform(transform) } - surfaces.front.layer_buffer_set.buffers = buffers; - } - Exit => { - *done = true; + + surfaces.front.layer_buffer_set.buffers = buffers } } } @@ -183,11 +186,12 @@ fn mainloop(po: Port, script_chan: SharedChan, opts: &Opts) { do window.set_composite_callback { do time::time(~"compositing") { + debug!("compositor: compositing"); // Adjust the layer dimensions as necessary to correspond to the size of the window. scene.size = window.size(); // Render the scene. - layers::rendergl::render_scene(context, scene); + rendergl::render_scene(context, scene); } window.present(); @@ -205,6 +209,19 @@ fn mainloop(po: Port, script_chan: SharedChan, opts: &Opts) { script_chan.send(LoadMsg(url::make_url(url_string.to_str(), None))) } + // When the user scrolls, move the layer around. + do window.set_scroll_callback |delta| { + // FIXME: Can't use `+=` due to a Rust bug. + let world_offset_copy = *world_offset; + *world_offset = world_offset_copy + delta; + + debug!("compositor: scrolled to %?", *world_offset); + + root_layer.common.set_transform(identity().translate(world_offset.x, world_offset.y, 0.0)); + + window.set_needs_display() + } + // Enter the main event loop. while !*done { // Check for new messages coming from the rendering task. @@ -230,47 +247,52 @@ struct SurfaceSet { back: Surface, } -fn lend_surface(surfaces: &mut SurfaceSet, receiver: Chan) { - // We are in a position to lend out the surface? - assert!(surfaces.front.have); - // Ok then take it - let old_layer_buffers = util::replace(&mut surfaces.front.layer_buffer_set.buffers, ~[]); - let new_layer_buffers = do old_layer_buffers.map |layer_buffer| { - let draw_target_ref = &layer_buffer.draw_target; - let layer_buffer = LayerBuffer { - draw_target: draw_target_ref.clone(), - rect: copy layer_buffer.rect, - stride: layer_buffer.stride +impl SurfaceSet { + /// Creates a new surface set. + fn new(backend: BackendType) -> SurfaceSet { + SurfaceSet { + front: Surface::new(backend), + back: Surface::new(backend), + } + } + + fn lend(&mut self, receiver: Chan) { + // We are in a position to lend out the surface? + assert!(self.front.have); + // Ok then take it + let old_layer_buffers = util::replace(&mut self.front.layer_buffer_set.buffers, ~[]); + let new_layer_buffers = do old_layer_buffers.map |layer_buffer| { + let draw_target_ref = &layer_buffer.draw_target; + let layer_buffer = LayerBuffer { + draw_target: draw_target_ref.clone(), + rect: copy layer_buffer.rect, + stride: layer_buffer.stride + }; + debug!("osmain: lending surface %?", layer_buffer); + layer_buffer }; - debug!("osmain: lending surface %?", layer_buffer); - layer_buffer - }; - surfaces.front.layer_buffer_set.buffers = old_layer_buffers; + self.front.layer_buffer_set.buffers = old_layer_buffers; - let new_layer_buffer_set = LayerBufferSet { buffers: new_layer_buffers }; - receiver.send(new_layer_buffer_set); - // Now we don't have it - surfaces.front.have = false; - // But we (hopefully) have another! - util::swap(&mut surfaces.front, &mut surfaces.back); - // Let's look - assert!(surfaces.front.have); -} + let new_layer_buffer_set = LayerBufferSet { buffers: new_layer_buffers }; + receiver.send(new_layer_buffer_set); + // Now we don't have it + self.front.have = false; + // But we (hopefully) have another! + util::swap(&mut self.front, &mut self.back); + // Let's look + assert!(self.front.have); + } -fn return_surface(surfaces: &mut SurfaceSet, layer_buffer_set: LayerBufferSet) { - //#debug("osmain: returning surface %?", layer_buffer_set); - // We have room for a return - assert!(surfaces.front.have); - assert!(!surfaces.back.have); + fn put_back(&mut self, layer_buffer_set: LayerBufferSet) { + // We have room for a return + assert!(self.front.have); + assert!(!self.back.have); - surfaces.back.layer_buffer_set = layer_buffer_set; + self.back.layer_buffer_set = layer_buffer_set; - // Now we have it again - surfaces.back.have = true; -} - -fn SurfaceSet(backend: BackendType) -> SurfaceSet { - SurfaceSet { front: Surface(backend), back: Surface(backend) } + // Now we have it again + self.back.have = true; + } } struct Surface { @@ -278,18 +300,20 @@ struct Surface { have: bool, } -fn Surface(backend: BackendType) -> Surface { - let layer_buffer = LayerBuffer { - draw_target: DrawTarget::new(backend, Size2D(800i32, 600i32), B8G8R8A8), - rect: Rect(Point2D(0u, 0u), Size2D(800u, 600u)), - stride: 800 * 4 - }; - let layer_buffer_set = LayerBufferSet { - buffers: ~[ layer_buffer ] - }; - Surface { - layer_buffer_set: layer_buffer_set, - have: true +impl Surface { + fn new(backend: BackendType) -> Surface { + let layer_buffer = LayerBuffer { + draw_target: DrawTarget::new(backend, Size2D(800, 600), B8G8R8A8), + rect: Rect(Point2D(0u, 0u), Size2D(800u, 600u)), + stride: 800 * 4 + }; + let layer_buffer_set = LayerBufferSet { + buffers: ~[ layer_buffer ] + }; + Surface { + layer_buffer_set: layer_buffer_set, + have: true + } } } diff --git a/src/components/servo/platform/common/glut_windowing.rs b/src/components/servo/platform/common/glut_windowing.rs index 167eae2dcf0..1e33c66b277 100644 --- a/src/components/servo/platform/common/glut_windowing.rs +++ b/src/components/servo/platform/common/glut_windowing.rs @@ -8,8 +8,11 @@ /// least on desktops. It is designed for testing Servo without the need of a UI. use windowing::{ApplicationMethods, CompositeCallback, LoadUrlCallback, ResizeCallback}; -use windowing::{WindowMethods}; +use windowing::{ScrollCallback, WindowMethods}; +use alert::{Alert, AlertMethods}; +use core::libc::c_int; +use geom::point::Point2D; use geom::size::Size2D; use glut::glut::{DOUBLE, WindowHeight, WindowWidth}; use glut::glut; @@ -32,6 +35,9 @@ pub struct Window { composite_callback: Option, resize_callback: Option, load_url_callback: Option, + scroll_callback: Option, + + drag_origin: Point2D, } impl WindowMethods for Window { @@ -48,6 +54,9 @@ impl WindowMethods for Window { composite_callback: None, resize_callback: None, load_url_callback: None, + scroll_callback: None, + + drag_origin: Point2D(0, 0), }; // Register event handlers. @@ -67,6 +76,12 @@ impl WindowMethods for Window { do glut::keyboard_func |key, _, _| { window.handle_key(key) } + do glut::mouse_func |_, _, x, y| { + window.start_drag(x, y) + } + do glut::motion_func |x, y| { + window.continue_drag(x, y) + } window } @@ -97,10 +112,20 @@ impl WindowMethods for Window { self.load_url_callback = Some(new_load_url_callback) } + /// Registers a callback to be run when the user scrolls. + pub fn set_scroll_callback(&mut self, new_scroll_callback: ScrollCallback) { + self.scroll_callback = Some(new_scroll_callback) + } + /// Spins the event loop. pub fn check_loop(@mut self) { glut::check_loop() } + + /// Schedules a redisplay. + pub fn set_needs_display(@mut self) { + glut::post_redisplay() + } } impl Window { @@ -112,11 +137,38 @@ impl Window { } } + /// Helper function to start a drag. + fn start_drag(&mut self, x: c_int, y: c_int) { + self.drag_origin = Point2D(x, y) + } + + /// Helper function to continue a drag. + fn continue_drag(&mut self, x: c_int, y: c_int) { + let new_point = Point2D(x, y); + let delta = new_point - self.drag_origin; + self.drag_origin = new_point; + + match self.scroll_callback { + None => {} + Some(callback) => callback(Point2D(delta.x as f32, delta.y as f32)), + } + } + /// Helper function to pop up an alert box prompting the user to load a URL. fn load_url(&self) { match self.load_url_callback { None => error!("no URL callback registered, doing nothing"), - Some(callback) => callback("http://purple.com/"), + Some(callback) => { + let mut alert: Alert = AlertMethods::new("Navigate to:"); + alert.add_prompt(); + alert.run(); + let value = alert.prompt_value(); + if "" == value { // To avoid crashing on Linux. + callback("http://purple.com/") + } else { + callback(value) + } + } } } } diff --git a/src/components/servo/servo.rc b/src/components/servo/servo.rc index 3a124522265..5a832000c7e 100755 --- a/src/components/servo/servo.rc +++ b/src/components/servo/servo.rc @@ -11,6 +11,7 @@ #[license = "MPL"]; #[crate_type = "lib"]; +extern mod alert; extern mod azure; extern mod geom; extern mod gfx (name = "servo_gfx"); diff --git a/src/components/servo/windowing.rs b/src/components/servo/windowing.rs index 8c642727a7f..708ea4105ec 100644 --- a/src/components/servo/windowing.rs +++ b/src/components/servo/windowing.rs @@ -4,6 +4,7 @@ //! Abstract windowing methods. The concrete implementations of these can be found in `platform/`. +use geom::point::Point2D; use geom::size::Size2D; /// Type of the function that is called when the screen is to be redisplayed. @@ -15,6 +16,9 @@ pub type ResizeCallback = @fn(uint, uint); /// Type of the function that is called when a new URL is to be loaded. pub type LoadUrlCallback = @fn(&str); +/// Type of the function that is called when the user scrolls. +pub type ScrollCallback = @fn(Point2D); + /// Methods for an abstract Application. pub trait ApplicationMethods { fn new() -> Self; @@ -34,8 +38,12 @@ pub trait WindowMethods { pub fn set_resize_callback(&mut self, new_resize_callback: ResizeCallback); /// Registers a callback to run when a new URL is to be loaded. pub fn set_load_url_callback(&mut self, new_load_url_callback: LoadUrlCallback); + /// Registers a callback to run when the user scrolls. + pub fn set_scroll_callback(&mut self, new_scroll_callback: ScrollCallback); /// Spins the event loop. pub fn check_loop(@mut self); + /// Schedules a redisplay at the next turn of the event loop. + pub fn set_needs_display(@mut self); } diff --git a/src/support/alert/rust-alert b/src/support/alert/rust-alert new file mode 160000 index 00000000000..0cba81aab48 --- /dev/null +++ b/src/support/alert/rust-alert @@ -0,0 +1 @@ +Subproject commit 0cba81aab4858442e9a995cf3b0149d0d116627c diff --git a/src/support/layers/rust-layers b/src/support/layers/rust-layers index 60036bce5d9..af960707afc 160000 --- a/src/support/layers/rust-layers +++ b/src/support/layers/rust-layers @@ -1 +1 @@ -Subproject commit 60036bce5d9962777467b6763c8642d9ce294cc7 +Subproject commit af960707afc666ac38591f1f0545f18a1bb89471