diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index 5baf7867dbbb..f274ba835d09 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -6,9 +6,11 @@ use platform::{Application, Window}; use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent, ResizeEvent}; use script::script_task::{LoadMsg, NavigateMsg, SendEventMsg}; use script::layout_interface::{LayoutChan, RouteScriptMsg}; -use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClickEvent}; -use windowing::{WindowMouseDownEvent, WindowMouseUpEvent}; +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, LayerBuffer, LayerBufferSet, RenderState}; use servo_msg::compositor_msg::{ReadyState, ScriptListener}; @@ -216,38 +218,39 @@ impl CompositorTask { let context = rendergl::init_render_context(); let root_layer = @mut ContainerLayer(); let window_size = window.size(); - let scene = @mut Scene(ContainerLayerKind(root_layer), window_size, identity()); - let done = @mut false; - let recomposite = @mut false; + let mut scene = Scene(ContainerLayerKind(root_layer), window_size, identity()); + let mut done = false; + let mut recomposite = false; // 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 page_size = @mut Size2D(0f32, 0f32); - let window_size = @mut Size2D(window_size.width as int, - window_size.height as int); + let mut world_offset = Point2D(0f32, 0f32); + let mut page_size = Size2D(0f32, 0f32); + let mut window_size = Size2D(window_size.width as int, + window_size.height as int); // Keeps track of the current zoom factor - let world_zoom = @mut 1f32; + let mut world_zoom = 1f32; // Keeps track of local zoom factor. Reset to 1 after a rerender event. - let local_zoom = @mut 1f32; + let mut local_zoom = 1f32; // Channel to the current renderer. // FIXME: This probably shouldn't be stored like this. - let render_chan: @mut Option = @mut None; - let pipeline_id: @mut Option = @mut None; + let mut render_chan: Option = None; + let mut pipeline_id: Option = None; + let mut layout_chan: Option = None; // Quadtree for this layer // FIXME: This should be one-per-layer - let quadtree: @mut Option> = @mut None; + let mut quadtree: Option> = None; // Keeps track of if we have performed a zoom event and how recently. - let zoom_action = @mut false; - let zoom_time = @mut 0f; + let mut zoom_action = false; + let mut zoom_time = 0f; // Extract tiles from the given quadtree and build and display the render tree. - let build_layer_tree: @fn(&Quadtree<~LayerBuffer>) = |quad: &Quadtree<~LayerBuffer>| { + let build_layer_tree: &fn(&Quadtree<~LayerBuffer>) = |quad: &Quadtree<~LayerBuffer>| { // Iterate over the children of the container layer. let mut current_layer_child = root_layer.first_child; @@ -292,31 +295,31 @@ impl CompositorTask { let origin = Point2D(origin.x as f32, origin.y as f32); // Set the layer's transform. - let transform = identity().translate(origin.x * *world_zoom, origin.y * *world_zoom, 0.0); - let transform = transform.scale(width as f32 * *world_zoom / buffer.resolution, height as f32 * *world_zoom / buffer.resolution, 1.0); + let transform = identity().translate(origin.x * world_zoom, origin.y * world_zoom, 0.0); + let transform = transform.scale(width as f32 * world_zoom / buffer.resolution, height as f32 * world_zoom / buffer.resolution, 1.0); texture_layer.common.set_transform(transform); } // Reset zoom - *local_zoom = 1f32; + local_zoom = 1f32; root_layer.common.set_transform(identity().translate(-world_offset.x, -world_offset.y, 0.0)); - *recomposite = true; + recomposite = true; }; - let ask_for_tiles: @fn() = || { - match *quadtree { + let ask_for_tiles = || { + match quadtree { Some(ref mut quad) => { let (tile_request, redisplay) = quad.get_tile_rects(Rect(Point2D(world_offset.x as int, world_offset.y as int), - *window_size), *world_zoom); + window_size), world_zoom); if !tile_request.is_empty() { - match *render_chan { + match render_chan { Some(ref chan) => { - chan.send(ReRenderMsg(tile_request, *world_zoom)); + chan.send(ReRenderMsg(tile_request, world_zoom)); } _ => { println("Warning: Compositor: Cannot send tile request, no render chan initialized"); @@ -331,68 +334,12 @@ impl CompositorTask { } } }; - - let update_layout_callbacks: @fn(LayoutChan) = |layout_chan: LayoutChan| { - let layout_chan_clone = layout_chan.clone(); - do window.set_navigation_callback |direction| { - let direction = match direction { - windowing::Forward => constellation_msg::Forward, - windowing::Back => constellation_msg::Back, - }; - layout_chan_clone.send(RouteScriptMsg(NavigateMsg(direction))); - } - - let layout_chan_clone = layout_chan.clone(); - // Hook the windowing system's resize callback up to the resize rate limiter. - do window.set_resize_callback |width, height| { - let new_size = Size2D(width as int, height as int); - if *window_size != new_size { - debug!("osmain: window resized to %ux%u", width, height); - *window_size = new_size; - layout_chan_clone.send(RouteScriptMsg(SendEventMsg(ResizeEvent(width, height)))); - } else { - debug!("osmain: dropping window resize since size is still %ux%u", width, height); - } - } - - let layout_chan_clone = layout_chan.clone(); - - // When the user enters a new URL, load it. - do window.set_load_url_callback |url_string| { - debug!("osmain: loading URL `%s`", url_string); - layout_chan_clone.send(RouteScriptMsg(LoadMsg(url::make_url(url_string.to_str(), None)))); - } - - let layout_chan_clone = layout_chan.clone(); - - // When the user triggers a mouse event, perform appropriate hit testing - do window.set_mouse_callback |window_mouse_event: WindowMouseEvent| { - let event: Event; - let world_mouse_point = |layer_mouse_point: Point2D| { - layer_mouse_point + *world_offset - }; - match window_mouse_event { - WindowClickEvent(button, layer_mouse_point) => { - event = ClickEvent(button, world_mouse_point(layer_mouse_point)); - } - WindowMouseDownEvent(button, layer_mouse_point) => { - event = MouseDownEvent(button, world_mouse_point(layer_mouse_point)); - - } - WindowMouseUpEvent(button, layer_mouse_point) => { - event = MouseUpEvent(button, world_mouse_point(layer_mouse_point)); - } - } - layout_chan_clone.send(RouteScriptMsg(SendEventMsg(event))); - } - }; - - - let check_for_messages: @fn(&Port) = |port: &Port| { + + let check_for_messages: &fn(&Port) = |port: &Port| { // Handle messages while port.peek() { match port.recv() { - Exit => *done = true, + Exit => done = true, ChangeReadyState(ready_state) => window.set_ready_state(ready_state), ChangeRenderState(render_state) => window.set_render_state(render_state), @@ -401,9 +348,9 @@ impl CompositorTask { new_render_chan, new_pipeline_id, response_chan) => { - update_layout_callbacks(new_layout_chan); - *render_chan = Some(new_render_chan); - *pipeline_id = Some(new_pipeline_id); + layout_chan = Some(new_layout_chan); + render_chan = Some(new_render_chan); + pipeline_id = Some(new_pipeline_id); response_chan.send(CompositorAck(new_pipeline_id)); } @@ -413,17 +360,17 @@ impl CompositorTask { } GetGLContext(chan) => chan.send(current_gl_context()), - + NewLayer(new_size, tile_size) => { - *page_size = Size2D(new_size.width as f32, new_size.height as f32); - *quadtree = Some(Quadtree::new(new_size.width.max(&(window_size.width as uint)), + page_size = Size2D(new_size.width as f32, new_size.height as f32); + quadtree = Some(Quadtree::new(new_size.width.max(&(window_size.width as uint)), new_size.height.max(&(window_size.height as uint)), tile_size, Some(10000000u))); ask_for_tiles(); } ResizeLayer(new_size) => { - *page_size = Size2D(new_size.width as f32, new_size.height as f32); + page_size = Size2D(new_size.width as f32, new_size.height as f32); // TODO: update quadtree, ask for tiles } DeleteLayer => { @@ -431,16 +378,15 @@ impl CompositorTask { } Paint(id, new_layer_buffer_set, new_size) => { - match *pipeline_id { + match pipeline_id { Some(pipeline_id) => if id != pipeline_id { loop; }, None => { loop; }, } debug!("osmain: received new frame"); - let quad; - match *quadtree { + match quadtree { Some(ref mut q) => quad = q, None => fail!("Compositor: given paint command with no quadtree initialized"), } @@ -452,10 +398,9 @@ impl CompositorTask { buffer.resolution, ~buffer.clone()); } - *page_size = Size2D(new_size.width as f32, new_size.height as f32); + page_size = Size2D(new_size.width as f32, new_size.height as f32); build_layer_tree(quad); - // TODO: Recycle the old buffers; send them back to the renderer to reuse if // it wishes. } @@ -463,6 +408,147 @@ impl CompositorTask { } }; + let check_for_window_messages: &fn(WindowEvent) = |event| { + match event { + IdleWindowEvent => {} + + ResizeWindowEvent(width, height) => { + let new_size = Size2D(width as int, height as int); + if window_size != new_size { + debug!("osmain: window resized to %ux%u", width, height); + window_size = new_size; + match layout_chan { + Some(ref chan) => chan.send(RouteScriptMsg(SendEventMsg(ResizeEvent(width, height)))), + 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); + match layout_chan { + Some(ref chan) => chan.send(RouteScriptMsg(LoadMsg(url::make_url(url_string.to_str(), None)))), + None => error!("Compositor: Recieved loadurl event without initialized layout chan"), + } + } + + MouseWindowEventClass(mouse_window_event) => { + let event: Event; + let world_mouse_point = |layer_mouse_point: Point2D| { + layer_mouse_point + world_offset + }; + match mouse_window_event { + MouseWindowClickEvent(button, layer_mouse_point) => { + event = ClickEvent(button, world_mouse_point(layer_mouse_point)); + } + MouseWindowMouseDownEvent(button, layer_mouse_point) => { + event = MouseDownEvent(button, world_mouse_point(layer_mouse_point)); + } + MouseWindowMouseUpEvent(button, layer_mouse_point) => { + event = MouseUpEvent(button, world_mouse_point(layer_mouse_point)); + } + } + match layout_chan { + Some(ref chan) => chan.send(RouteScriptMsg(SendEventMsg(event))), + None => error!("Compositor: Recieved mouse event without initialized layout chan"), + } + } + + ScrollWindowEvent(delta) => { + // FIXME (Rust #2528): Can't use `-=`. + let world_offset_copy = world_offset; + world_offset = world_offset_copy - delta; + + // Clamp the world offset to the screen size. + let max_x = (page_size.width * world_zoom - window_size.width as f32).max(&0.0); + world_offset.x = world_offset.x.clamp(&0.0, &max_x).round(); + let max_y = (page_size.height * world_zoom - window_size.height as f32).max(&0.0); + world_offset.y = world_offset.y.clamp(&0.0, &max_y).round(); + + debug!("compositor: scrolled to %?", world_offset); + + + let mut scroll_transform = identity(); + + scroll_transform = scroll_transform.translate(window_size.width as f32 / 2f32 * local_zoom - world_offset.x, + window_size.height as f32 / 2f32 * local_zoom - world_offset.y, + 0.0); + scroll_transform = scroll_transform.scale(local_zoom, local_zoom, 1f32); + scroll_transform = scroll_transform.translate(window_size.width as f32 / -2f32, + window_size.height as f32 / -2f32, + 0.0); + + root_layer.common.set_transform(scroll_transform); + + ask_for_tiles(); + + recomposite = true; + } + + 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); + local_zoom = local_zoom * world_zoom/old_world_zoom; + + // Update world offset + let corner_to_center_x = world_offset.x + window_size.width as f32 / 2f32; + let new_corner_to_center_x = corner_to_center_x * world_zoom / old_world_zoom; + world_offset.x = world_offset.x + new_corner_to_center_x - corner_to_center_x; + + let corner_to_center_y = world_offset.y + window_size.height as f32 / 2f32; + let new_corner_to_center_y = corner_to_center_y * world_zoom / old_world_zoom; + world_offset.y = world_offset.y + new_corner_to_center_y - corner_to_center_y; + + // Clamp to page bounds when zooming out + let max_x = (page_size.width * world_zoom - window_size.width as f32).max(&0.0); + world_offset.x = world_offset.x.clamp(&0.0, &max_x).round(); + let max_y = (page_size.height * world_zoom - window_size.height as f32).max(&0.0); + world_offset.y = world_offset.y.clamp(&0.0, &max_y).round(); + + // Apply transformations + let mut zoom_transform = identity(); + zoom_transform = zoom_transform.translate(window_size.width as f32 / 2f32 * local_zoom - world_offset.x, + window_size.height as f32 / 2f32 * local_zoom - world_offset.y, + 0.0); + zoom_transform = zoom_transform.scale(local_zoom, local_zoom, 1f32); + zoom_transform = zoom_transform.translate(window_size.width as f32 / -2f32, + window_size.height as f32 / -2f32, + 0.0); + root_layer.common.set_transform(zoom_transform); + + recomposite = true; + } + + NavigationWindowEvent(direction) => { + let direction = match direction { + windowing::Forward => constellation_msg::Forward, + windowing::Back => constellation_msg::Back, + }; + match layout_chan { + Some(ref chan) => chan.send(RouteScriptMsg(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; @@ -473,7 +559,7 @@ impl CompositorTask { scene.size = window.size(); // Render the scene. - rendergl::render_scene(context, scene); + rendergl::render_scene(context, &scene); } // Render to PNG. We must read from the back buffer (ie, before @@ -504,105 +590,32 @@ impl CompositorTask { let res = png::store_png(&img, &path); assert!(res.is_ok()); - *done = true; + done = true; } window.present(); - if exit { *done = true; } + if exit { done = true; } }; - // When the user scrolls, move the layer around. - do window.set_scroll_callback |delta| { - // FIXME (Rust #2528): Can't use `-=`. - let world_offset_copy = *world_offset; - *world_offset = world_offset_copy - delta; - - // Clamp the world offset to the screen size. - let max_x = (page_size.width * *world_zoom - window_size.width as f32).max(&0.0); - world_offset.x = world_offset.x.clamp(&0.0, &max_x).round(); - let max_y = (page_size.height * *world_zoom - window_size.height as f32).max(&0.0); - world_offset.y = world_offset.y.clamp(&0.0, &max_y).round(); - - debug!("compositor: scrolled to %?", *world_offset); - - - let mut scroll_transform = identity(); - - scroll_transform = scroll_transform.translate(window_size.width as f32 / 2f32 * *local_zoom - world_offset.x, - window_size.height as f32 / 2f32 * *local_zoom - world_offset.y, - 0.0); - scroll_transform = scroll_transform.scale(*local_zoom, *local_zoom, 1f32); - scroll_transform = scroll_transform.translate(window_size.width as f32 / -2f32, - window_size.height as f32 / -2f32, - 0.0); - - root_layer.common.set_transform(scroll_transform); - - ask_for_tiles(); - - *recomposite = true; - } - - // When the user pinch-zooms, scale the layer - do window.set_zoom_callback |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); - *local_zoom = *local_zoom * *world_zoom/old_world_zoom; - - // Update world offset - let corner_to_center_x = world_offset.x + window_size.width as f32 / 2f32; - let new_corner_to_center_x = corner_to_center_x * *world_zoom / old_world_zoom; - world_offset.x = world_offset.x + new_corner_to_center_x - corner_to_center_x; - - let corner_to_center_y = world_offset.y + window_size.height as f32 / 2f32; - let new_corner_to_center_y = corner_to_center_y * *world_zoom / old_world_zoom; - world_offset.y = world_offset.y + new_corner_to_center_y - corner_to_center_y; - - // Clamp to page bounds when zooming out - let max_x = (page_size.width * *world_zoom - window_size.width as f32).max(&0.0); - world_offset.x = world_offset.x.clamp(&0.0, &max_x).round(); - let max_y = (page_size.height * *world_zoom - window_size.height as f32).max(&0.0); - world_offset.y = world_offset.y.clamp(&0.0, &max_y).round(); - - // Apply transformations - let mut zoom_transform = identity(); - zoom_transform = zoom_transform.translate(window_size.width as f32 / 2f32 * *local_zoom - world_offset.x, - window_size.height as f32 / 2f32 * *local_zoom - world_offset.y, - 0.0); - zoom_transform = zoom_transform.scale(*local_zoom, *local_zoom, 1f32); - zoom_transform = zoom_transform.translate(window_size.width as f32 / -2f32, - window_size.height as f32 / -2f32, - 0.0); - root_layer.common.set_transform(zoom_transform); - - *recomposite = true; - } - // Enter the main event loop. - while !*done { + while !done { // Check for new messages coming from the rendering task. check_for_messages(&self.port); // Check for messages coming from the windowing system. - if window.check_loop() { - *done = true; - } + check_for_window_messages(window.recv()); - if *recomposite { - *recomposite = false; + if recomposite { + recomposite = false; composite(); } timer::sleep(&uv_global_loop::get(), 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; + if zoom_action && precise_time_s() - zoom_time > 0.3 { + zoom_action = false; ask_for_tiles(); } diff --git a/src/components/main/platform/common/glfw_windowing.rs b/src/components/main/platform/common/glfw_windowing.rs index 0c7adb871897..10be6d5b2e8e 100644 --- a/src/components/main/platform/common/glfw_windowing.rs +++ b/src/components/main/platform/common/glfw_windowing.rs @@ -4,9 +4,11 @@ //! A windowing implementation using GLFW. -use windowing::{ApplicationMethods, LoadUrlCallback, MouseCallback, FinishedCallback}; -use windowing::{ResizeCallback, ScrollCallback, WindowMethods, WindowMouseEvent, WindowClickEvent}; -use windowing::{WindowMouseDownEvent, WindowMouseUpEvent, ZoomCallback, Forward, Back, NavigationCallback}; +use windowing::{ApplicationMethods, WindowEvent, WindowMethods}; +use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass}; +use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent}; +use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent}; +use windowing::{Forward, Back}; use alert::{Alert, AlertMethods}; use std::libc::c_int; @@ -39,13 +41,7 @@ impl Drop for Application { pub struct Window { glfw_window: glfw::Window, - resize_callback: Option, - load_url_callback: Option, - mouse_callback: Option, - scroll_callback: Option, - zoom_callback: Option, - navigation_callback: Option, - finished_callback: Option, + event_queue: @mut ~[WindowEvent], drag_origin: Point2D, @@ -59,7 +55,7 @@ pub struct Window { impl WindowMethods for Window { /// Creates a new window. - pub fn new(_: &Application) -> @mut Window { + fn new(_: &Application) -> @mut Window { // Create the GLFW window. let glfw_window = glfw::Window::create(800, 600, "Servo", glfw::Windowed).unwrap(); glfw_window.make_context_current(); @@ -68,13 +64,7 @@ impl WindowMethods for Window { let window = @mut Window { glfw_window: glfw_window, - resize_callback: None, - load_url_callback: None, - mouse_callback: None, - scroll_callback: None, - zoom_callback: None, - navigation_callback: None, - finished_callback: None, + event_queue: @mut ~[], drag_origin: Point2D(0 as c_int, 0), @@ -86,12 +76,11 @@ impl WindowMethods for Window { throbber_frame: 0, }; + let event_queue = window.event_queue; + // Register event handlers. do window.glfw_window.set_framebuffer_size_callback |_win, width, height| { - match window.resize_callback { - None => {} - Some(callback) => callback(width as uint, height as uint), - } + event_queue.push(ResizeWindowEvent(width as uint, height as uint)) } do window.glfw_window.set_key_callback |_win, key, _scancode, action, mods| { if action == glfw::PRESS { @@ -108,81 +97,52 @@ impl WindowMethods for Window { let dx = (x_offset as f32) * 30.0; let dy = (y_offset as f32) * 30.0; - window.handle_scroll(Point2D(dx, dy)); + event_queue.push(ScrollWindowEvent(Point2D(dx, dy))); } window } /// Returns the size of the window. - pub fn size(&self) -> Size2D { + fn size(&self) -> Size2D { let (width, height) = self.glfw_window.get_framebuffer_size(); Size2D(width as f32, height as f32) } /// Presents the window to the screen (perhaps by page flipping). - pub fn present(&mut self) { + fn present(&mut self) { self.glfw_window.swap_buffers(); } - - /// Registers a callback to run when a resize event occurs. - pub fn set_resize_callback(&mut self, new_resize_callback: ResizeCallback) { - self.resize_callback = Some(new_resize_callback) - } - - /// Registers a callback to be run when a new URL is to be loaded. - pub fn set_load_url_callback(&mut self, new_load_url_callback: LoadUrlCallback) { - self.load_url_callback = Some(new_load_url_callback) - } - - /// Registers a callback to be run when a mouse event occurs. - pub fn set_mouse_callback(&mut self, new_mouse_callback: MouseCallback) { - self.mouse_callback = Some(new_mouse_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) - } - - /// Registers a zoom to be run when the user zooms. - pub fn set_zoom_callback(&mut self, new_zoom_callback: ZoomCallback) { - self.zoom_callback = Some(new_zoom_callback) - } - - /// Registers a callback to be run when backspace or shift-backspace is pressed. - pub fn set_navigation_callback(&mut self, new_navigation_callback: NavigationCallback) { - self.navigation_callback = Some(new_navigation_callback) - } - - pub fn set_finished_callback(&mut self, new_finished_callback: FinishedCallback) { - self.finished_callback = Some(new_finished_callback) - } - - /// Spins the event loop. - pub fn check_loop(@mut self) -> bool { + + fn recv(@mut self) -> WindowEvent { + if !self.event_queue.is_empty() { + return self.event_queue.shift() + } glfw::poll_events(); self.throbber_frame = (self.throbber_frame + 1) % (THROBBER.len() as u8); self.update_window_title(); - self.glfw_window.should_close() + if self.glfw_window.should_close() { + QuitWindowEvent + } else if !self.event_queue.is_empty() { + self.event_queue.shift() + } else { + IdleWindowEvent + } } /// Sets the ready state. - pub fn set_ready_state(@mut self, ready_state: ReadyState) { + fn set_ready_state(@mut self, ready_state: ReadyState) { self.ready_state = ready_state; self.update_window_title() } /// Sets the render state. - pub fn set_render_state(@mut self, render_state: RenderState) { + fn set_render_state(@mut self, render_state: RenderState) { if self.ready_state == FinishedLoading && self.render_state == RenderingRenderState && render_state == IdleRenderState { - // page loaded - for self.finished_callback.iter().advance |&callback| { - callback(); - } + self.event_queue.push(FinishedWindowEvent); } self.render_state = render_state; @@ -221,24 +181,16 @@ impl Window { glfw::KEY_ESCAPE => self.glfw_window.set_should_close(true), glfw::KEY_L if mods & glfw::MOD_CONTROL != 0 => self.load_url(), // Ctrl+L glfw::KEY_EQUAL if mods & glfw::MOD_CONTROL != 0 => { // Ctrl-+ - for self.zoom_callback.iter().advance |&callback| { - callback(1.1); - } + self.event_queue.push(ZoomWindowEvent(1.1)); } glfw::KEY_MINUS if mods & glfw::MOD_CONTROL != 0 => { // Ctrl-- - for self.zoom_callback.iter().advance |&callback| { - callback(0.90909090909); - } + self.event_queue.push(ZoomWindowEvent(0.90909090909)); } glfw::KEY_BACKSPACE if mods & glfw::MOD_SHIFT != 0 => { // Shift-Backspace - for self.navigation_callback.iter().advance |&callback| { - callback(Forward); - } + self.event_queue.push(NavigationWindowEvent(Forward)); } glfw::KEY_BACKSPACE => { // Backspace - for self.navigation_callback.iter().advance |&callback| { - callback(Back); - } + self.event_queue.push(NavigationWindowEvent(Back)); } _ => {} } @@ -248,67 +200,40 @@ impl Window { fn handle_mouse(&self, button: c_int, action: c_int, x: c_int, y: c_int) { // FIXME(tkuehn): max pixel dist should be based on pixel density let max_pixel_dist = 10f; - match self.mouse_callback { - None => {} - Some(callback) => { - let event: WindowMouseEvent; - match action { - glfw::PRESS => { - event = WindowMouseDownEvent(button as uint, Point2D(x as f32, y as f32)); - *self.mouse_down_point = Point2D(x, y); - *self.mouse_down_button = button; - } - glfw::RELEASE => { - event = WindowMouseUpEvent(button as uint, Point2D(x as f32, y as f32)); - if *self.mouse_down_button == button { - let pixel_dist = *self.mouse_down_point - Point2D(x, y); - let pixel_dist = ((pixel_dist.x * pixel_dist.x + - pixel_dist.y * pixel_dist.y) as float).sqrt(); - if pixel_dist < max_pixel_dist { - let click_event = WindowClickEvent(button as uint, - Point2D(x as f32, y as f32)); - callback(click_event); - } - } + let event = match action { + glfw::PRESS => { + *self.mouse_down_point = Point2D(x, y); + *self.mouse_down_button = button; + MouseWindowMouseDownEvent(button as uint, Point2D(x as f32, y as f32)) + } + glfw::RELEASE => { + if *self.mouse_down_button == button { + let pixel_dist = *self.mouse_down_point - Point2D(x, y); + let pixel_dist = ((pixel_dist.x * pixel_dist.x + + pixel_dist.y * pixel_dist.y) as float).sqrt(); + if pixel_dist < max_pixel_dist { + let click_event = MouseWindowClickEvent(button as uint, + Point2D(x as f32, y as f32)); + self.event_queue.push(MouseWindowEventClass(click_event)); } - _ => fail!("I cannot recognize the type of mouse action that occured. :-(") - }; - callback(event); + } + MouseWindowMouseUpEvent(button as uint, Point2D(x as f32, y as f32)) } - } - } - - /// Helper function to handle a scroll. - fn handle_scroll(&mut self, delta: Point2D) { - match self.scroll_callback { - None => {} - Some(callback) => callback(delta), - } - } - - /// Helper function to handle a zoom. - fn handle_zoom(&mut self, magnification: f32) { - match self.zoom_callback { - None => {} - Some(callback) => callback(magnification), - } + _ => fail!("I cannot recognize the type of mouse action that occured. :-(") + }; + self.event_queue.push(MouseWindowEventClass(event)); } /// 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) => { - 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) - } - } + 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. + self.event_queue.push(LoadUrlWindowEvent(~"http://purple.com/")) + } else { + self.event_queue.push(LoadUrlWindowEvent(value)) } } } diff --git a/src/components/main/windowing.rs b/src/components/main/windowing.rs index cee4850cc4f4..00439009d346 100644 --- a/src/components/main/windowing.rs +++ b/src/components/main/windowing.rs @@ -8,10 +8,10 @@ use geom::point::Point2D; use geom::size::Size2D; use servo_msg::compositor_msg::{ReadyState, RenderState}; -pub enum WindowMouseEvent { - WindowClickEvent(uint, Point2D), - WindowMouseDownEvent(uint, Point2D), - WindowMouseUpEvent(uint, Point2D), +pub enum MouseWindowEvent { + MouseWindowClickEvent(uint, Point2D), + MouseWindowMouseDownEvent(uint, Point2D), + MouseWindowMouseUpEvent(uint, Point2D), } pub enum WindowNavigateMsg { @@ -19,26 +19,30 @@ pub enum WindowNavigateMsg { Back, } -/// Type of the function that is called when the window is resized. -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 a mouse hit test is to be performed. -pub type MouseCallback = @fn(WindowMouseEvent); - -/// Type of the function that is called when the user scrolls. -pub type ScrollCallback = @fn(Point2D); - -/// Type of the function that is called when the user zooms. -pub type ZoomCallback = @fn(f32); - -/// Type of the function that is called when the user clicks backspace or shift-backspace -pub type NavigationCallback = @fn(WindowNavigateMsg); - -/// Type of the function that is called when the rendering is finished -pub type FinishedCallback = @fn(); +/// Events that the windowing system sends to Servo. +pub enum WindowEvent { + /// Sent when no message has arrived. + /// + /// FIXME: This is a bogus event and is only used because we don't have the new + /// scheduler integrated with the platform event loop. + IdleWindowEvent, + /// Sent when the window is resized. + ResizeWindowEvent(uint, uint), + /// Sent when a new URL is to be loaded. + LoadUrlWindowEvent(~str), + /// Sent when a mouse hit test is to be performed. + MouseWindowEventClass(MouseWindowEvent), + /// Sent when the user scrolls. + ScrollWindowEvent(Point2D), + /// Sent when the user zooms. + ZoomWindowEvent(f32), + /// Sent when the user uses chrome navigation (i.e. backspace or shift-backspace). + NavigationWindowEvent(WindowNavigateMsg), + /// Sent when rendering is finished. + FinishedWindowEvent, + /// Sent when the user quits the application + QuitWindowEvent, +} /// Methods for an abstract Application. pub trait ApplicationMethods { @@ -52,24 +56,10 @@ pub trait WindowMethods { pub fn size(&self) -> Size2D; /// Presents the window to the screen (perhaps by page flipping). pub fn present(&mut self); + + /// Spins the event loop and returns the next event. + pub fn recv(@mut self) -> WindowEvent; - /// Registers a callback to run when a resize event occurs. - 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 clicks. - pub fn set_mouse_callback(&mut self, new_mouse_callback: MouseCallback); - /// Registers a callback to run when the user scrolls. - pub fn set_scroll_callback(&mut self, new_scroll_callback: ScrollCallback); - /// Registers a callback to run when the user zooms. - pub fn set_zoom_callback(&mut self, new_zoom_callback: ZoomCallback); - /// Registers a callback to run when the user presses backspace or shift-backspace. - pub fn set_navigation_callback(&mut self, new_navigation_callback: NavigationCallback); - /// Registers a callback to run when rendering is finished. - pub fn set_finished_callback(&mut self, new_finish_callback: FinishedCallback); - - /// Spins the event loop. Returns whether the window should close. - pub fn check_loop(@mut self) -> bool; /// Sets the ready state of the current page. pub fn set_ready_state(@mut self, ready_state: ReadyState); /// Sets the render state of the current page.