mirror of
https://github.com/servo/servo.git
synced 2025-06-24 09:04:33 +01:00
Refactor compositor to use messages rather than callbacks.
This commit is contained in:
parent
c17ede3716
commit
d695b2d2bc
3 changed files with 285 additions and 357 deletions
|
@ -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<RenderChan> = @mut None;
|
||||
let pipeline_id: @mut Option<uint> = @mut None;
|
||||
let mut render_chan: Option<RenderChan> = None;
|
||||
let mut pipeline_id: Option<uint> = None;
|
||||
let mut layout_chan: Option<LayoutChan> = None;
|
||||
|
||||
// Quadtree for this layer
|
||||
// FIXME: This should be one-per-layer
|
||||
let quadtree: @mut Option<Quadtree<~LayerBuffer>> = @mut None;
|
||||
let mut quadtree: Option<Quadtree<~LayerBuffer>> = 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<f32>| {
|
||||
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<Msg>) = |port: &Port<Msg>| {
|
||||
|
||||
let check_for_messages: &fn(&Port<Msg>) = |port: &Port<Msg>| {
|
||||
// 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<f32>| {
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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<ResizeCallback>,
|
||||
load_url_callback: Option<LoadUrlCallback>,
|
||||
mouse_callback: Option<MouseCallback>,
|
||||
scroll_callback: Option<ScrollCallback>,
|
||||
zoom_callback: Option<ZoomCallback>,
|
||||
navigation_callback: Option<NavigationCallback>,
|
||||
finished_callback: Option<FinishedCallback>,
|
||||
event_queue: @mut ~[WindowEvent],
|
||||
|
||||
drag_origin: Point2D<c_int>,
|
||||
|
||||
|
@ -59,7 +55,7 @@ pub struct Window {
|
|||
|
||||
impl WindowMethods<Application> 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<Application> 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<Application> 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<Application> 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<f32> {
|
||||
fn size(&self) -> Size2D<f32> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => fail!("I cannot recognize the type of mouse action that occured. :-(")
|
||||
};
|
||||
callback(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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to handle a scroll.
|
||||
fn handle_scroll(&mut self, delta: Point2D<f32>) {
|
||||
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),
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
MouseWindowMouseUpEvent(button as uint, Point2D(x as f32, y as f32))
|
||||
}
|
||||
_ => 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<f32>),
|
||||
WindowMouseDownEvent(uint, Point2D<f32>),
|
||||
WindowMouseUpEvent(uint, Point2D<f32>),
|
||||
pub enum MouseWindowEvent {
|
||||
MouseWindowClickEvent(uint, Point2D<f32>),
|
||||
MouseWindowMouseDownEvent(uint, Point2D<f32>),
|
||||
MouseWindowMouseUpEvent(uint, Point2D<f32>),
|
||||
}
|
||||
|
||||
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<f32>);
|
||||
|
||||
/// 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<f32>),
|
||||
/// 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<A> {
|
|||
pub fn size(&self) -> Size2D<f32>;
|
||||
/// 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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue