mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
decouple script from compositor
communicate via layout refactor channel wrappers from *Task --> *Chan fix merge fallout
This commit is contained in:
parent
93eea6b2e8
commit
b5dac3f426
17 changed files with 609 additions and 538 deletions
|
@ -34,7 +34,7 @@ pub enum RenderState {
|
||||||
|
|
||||||
/// The interface used to by the renderer to acquire draw targets for each rendered frame and
|
/// The interface used to by the renderer to acquire draw targets for each rendered frame and
|
||||||
/// submit them to be drawn to the display.
|
/// submit them to be drawn to the display.
|
||||||
pub trait Compositor {
|
pub trait RenderListener {
|
||||||
fn get_gl_context(&self) -> AzGLContext;
|
fn get_gl_context(&self) -> AzGLContext;
|
||||||
fn paint(&self, layer_buffer_set: LayerBufferSet, new_size: Size2D<uint>);
|
fn paint(&self, layer_buffer_set: LayerBufferSet, new_size: Size2D<uint>);
|
||||||
fn set_render_state(&self, render_state: RenderState);
|
fn set_render_state(&self, render_state: RenderState);
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
// The task that handles all rendering/painting.
|
// The task that handles all rendering/painting.
|
||||||
|
|
||||||
use azure::{AzFloat, AzGLContext};
|
use azure::{AzFloat, AzGLContext};
|
||||||
use compositor::{Compositor, IdleRenderState, RenderingRenderState};
|
use compositor::{RenderListener, IdleRenderState, RenderingRenderState};
|
||||||
use font_context::FontContext;
|
use font_context::FontContext;
|
||||||
use geom::matrix2d::Matrix2D;
|
use geom::matrix2d::Matrix2D;
|
||||||
use opts::Opts;
|
use opts::Opts;
|
||||||
|
@ -28,66 +28,70 @@ pub enum Msg {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
pub struct RenderTask {
|
pub struct RenderChan {
|
||||||
channel: SharedChan<Msg>,
|
chan: SharedChan<Msg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderTask {
|
impl RenderChan {
|
||||||
pub fn new<C:Compositor + Owned>(compositor: C,
|
pub fn new(chan: Chan<Msg>) -> RenderChan {
|
||||||
opts: Opts,
|
RenderChan {
|
||||||
profiler_chan: ProfilerChan)
|
chan: SharedChan::new(chan),
|
||||||
-> RenderTask {
|
|
||||||
let compositor_cell = Cell(compositor);
|
|
||||||
let opts_cell = Cell(opts);
|
|
||||||
let (port, chan) = comm::stream();
|
|
||||||
let port = Cell(port);
|
|
||||||
|
|
||||||
do spawn {
|
|
||||||
let compositor = compositor_cell.take();
|
|
||||||
let share_gl_context = compositor.get_gl_context();
|
|
||||||
|
|
||||||
// FIXME: Annoying three-cell dance here. We need one-shot closures.
|
|
||||||
let opts = opts_cell.with_ref(|o| copy *o);
|
|
||||||
let n_threads = opts.n_render_threads;
|
|
||||||
let new_opts_cell = Cell(opts);
|
|
||||||
|
|
||||||
let profiler_chan = profiler_chan.clone();
|
|
||||||
let profiler_chan_copy = profiler_chan.clone();
|
|
||||||
|
|
||||||
let thread_pool = do TaskPool::new(n_threads, Some(SingleThreaded)) {
|
|
||||||
let opts_cell = Cell(new_opts_cell.with_ref(|o| copy *o));
|
|
||||||
let profiler_chan = Cell(profiler_chan.clone());
|
|
||||||
|
|
||||||
let f: ~fn(uint) -> ThreadRenderContext = |thread_index| {
|
|
||||||
let opts = opts_cell.with_ref(|opts| copy *opts);
|
|
||||||
|
|
||||||
ThreadRenderContext {
|
|
||||||
thread_index: thread_index,
|
|
||||||
font_ctx: @mut FontContext::new(opts.render_backend,
|
|
||||||
false,
|
|
||||||
profiler_chan.take()),
|
|
||||||
opts: opts,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
f
|
|
||||||
};
|
|
||||||
|
|
||||||
// FIXME: rust/#5967
|
|
||||||
let mut renderer = Renderer {
|
|
||||||
port: port.take(),
|
|
||||||
compositor: compositor,
|
|
||||||
thread_pool: thread_pool,
|
|
||||||
opts: opts_cell.take(),
|
|
||||||
profiler_chan: profiler_chan_copy,
|
|
||||||
share_gl_context: share_gl_context,
|
|
||||||
};
|
|
||||||
|
|
||||||
renderer.start();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
pub fn send(&self, msg: Msg) {
|
||||||
|
self.chan.send(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RenderTask {
|
pub fn create_render_task<C: RenderListener + Owned>(port: Port<Msg>,
|
||||||
channel: SharedChan::new(chan),
|
compositor: C,
|
||||||
}
|
opts: Opts,
|
||||||
|
profiler_chan: ProfilerChan) {
|
||||||
|
let compositor_cell = Cell(compositor);
|
||||||
|
let opts_cell = Cell(opts);
|
||||||
|
let port = Cell(port);
|
||||||
|
|
||||||
|
do spawn {
|
||||||
|
let compositor = compositor_cell.take();
|
||||||
|
let share_gl_context = compositor.get_gl_context();
|
||||||
|
|
||||||
|
// FIXME: Annoying three-cell dance here. We need one-shot closures.
|
||||||
|
let opts = opts_cell.with_ref(|o| copy *o);
|
||||||
|
let n_threads = opts.n_render_threads;
|
||||||
|
let new_opts_cell = Cell(opts);
|
||||||
|
|
||||||
|
let profiler_chan = profiler_chan.clone();
|
||||||
|
let profiler_chan_copy = profiler_chan.clone();
|
||||||
|
|
||||||
|
let thread_pool = do TaskPool::new(n_threads, Some(SingleThreaded)) {
|
||||||
|
let opts_cell = Cell(new_opts_cell.with_ref(|o| copy *o));
|
||||||
|
let profiler_chan = Cell(profiler_chan.clone());
|
||||||
|
|
||||||
|
let f: ~fn(uint) -> ThreadRenderContext = |thread_index| {
|
||||||
|
let opts = opts_cell.with_ref(|opts| copy *opts);
|
||||||
|
|
||||||
|
ThreadRenderContext {
|
||||||
|
thread_index: thread_index,
|
||||||
|
font_ctx: @mut FontContext::new(opts.render_backend,
|
||||||
|
false,
|
||||||
|
profiler_chan.take()),
|
||||||
|
opts: opts,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
f
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: rust/#5967
|
||||||
|
let mut renderer = Renderer {
|
||||||
|
port: port.take(),
|
||||||
|
compositor: compositor,
|
||||||
|
thread_pool: thread_pool,
|
||||||
|
opts: opts_cell.take(),
|
||||||
|
profiler_chan: profiler_chan_copy,
|
||||||
|
share_gl_context: share_gl_context,
|
||||||
|
};
|
||||||
|
|
||||||
|
renderer.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +114,7 @@ priv struct Renderer<C> {
|
||||||
share_gl_context: AzGLContext,
|
share_gl_context: AzGLContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Compositor + Owned> Renderer<C> {
|
impl<C: RenderListener + Owned> Renderer<C> {
|
||||||
fn start(&mut self) {
|
fn start(&mut self) {
|
||||||
debug!("renderer: beginning rendering loop");
|
debug!("renderer: beginning rendering loop");
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,15 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use compositing::resize_rate_limiter::ResizeRateLimiter;
|
|
||||||
use platform::{Application, Window};
|
use platform::{Application, Window};
|
||||||
use script::script_task::{LoadMsg, ScriptMsg, SendEventMsg};
|
use script::script_task::{LoadMsg, SendEventMsg};
|
||||||
use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClickEvent};
|
use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClickEvent};
|
||||||
use windowing::{WindowMouseDownEvent, WindowMouseUpEvent};
|
use windowing::{WindowMouseDownEvent, WindowMouseUpEvent};
|
||||||
|
|
||||||
use gfx::compositor::RenderState;
|
use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent, ResizeEvent};
|
||||||
use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent};
|
use script::compositor_interface::{ReadyState, ScriptListener};
|
||||||
use script::compositor_interface::{ReadyState, CompositorInterface};
|
use script::script_task::{ScriptChan, SendEventMsg};
|
||||||
use script::compositor_interface;
|
use script::layout_interface::{LayoutChan, RouteScriptMsg};
|
||||||
|
|
||||||
use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context};
|
use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context};
|
||||||
use azure::azure::AzGLContext;
|
use azure::azure::AzGLContext;
|
||||||
|
@ -22,7 +21,7 @@ use core::util;
|
||||||
use geom::matrix::identity;
|
use geom::matrix::identity;
|
||||||
use geom::point::Point2D;
|
use geom::point::Point2D;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use gfx::compositor::{Compositor, LayerBufferSet, RenderState};
|
use gfx::compositor::{RenderListener, LayerBufferSet, RenderState};
|
||||||
use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format};
|
use layers::layers::{ARGB32Format, ContainerLayer, ContainerLayerKind, Format};
|
||||||
use layers::layers::{ImageData, WithDataFn};
|
use layers::layers::{ImageData, WithDataFn};
|
||||||
use layers::layers::{TextureLayerKind, TextureLayer, TextureManager};
|
use layers::layers::{TextureLayerKind, TextureLayer, TextureManager};
|
||||||
|
@ -32,43 +31,44 @@ use servo_util::{time, url};
|
||||||
use servo_util::time::profile;
|
use servo_util::time::profile;
|
||||||
use servo_util::time::ProfilerChan;
|
use servo_util::time::ProfilerChan;
|
||||||
|
|
||||||
mod resize_rate_limiter;
|
|
||||||
|
|
||||||
/// The implementation of the layers-based compositor.
|
/// The implementation of the layers-based compositor.
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
pub struct CompositorTask {
|
pub struct CompositorChan {
|
||||||
/// A channel on which messages can be sent to the compositor.
|
/// A channel on which messages can be sent to the compositor.
|
||||||
chan: SharedChan<Msg>,
|
chan: SharedChan<Msg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompositorInterface for CompositorTask {
|
/// Implementation of the abstract `ScriptListener` interface.
|
||||||
|
impl ScriptListener for CompositorChan {
|
||||||
fn set_ready_state(&self, ready_state: ReadyState) {
|
fn set_ready_state(&self, ready_state: ReadyState) {
|
||||||
let msg = ChangeReadyState(ready_state);
|
let msg = ChangeReadyState(ready_state);
|
||||||
self.chan.send(msg);
|
self.chan.send(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompositorTask {
|
/// Implementation of the abstract `RenderListener` interface.
|
||||||
/// Starts the compositor. Returns an interface that can be used to communicate with the
|
impl RenderListener for CompositorChan {
|
||||||
/// compositor and a port which allows notification when the compositor shuts down.
|
fn get_gl_context(&self) -> AzGLContext {
|
||||||
pub fn new(script_chan: SharedChan<ScriptMsg>, profiler_chan: ProfilerChan)
|
let (port, chan) = comm::stream();
|
||||||
-> (CompositorTask, Port<()>) {
|
self.chan.send(GetGLContext(chan));
|
||||||
let script_chan = Cell(script_chan);
|
port.recv()
|
||||||
let (shutdown_port, shutdown_chan) = stream();
|
}
|
||||||
let shutdown_chan = Cell(shutdown_chan);
|
fn paint(&self, layer_buffer_set: LayerBufferSet, new_size: Size2D<uint>) {
|
||||||
|
self.chan.send(Paint(layer_buffer_set, new_size))
|
||||||
|
}
|
||||||
|
fn set_render_state(&self, render_state: RenderState) {
|
||||||
|
self.chan.send(ChangeRenderState(render_state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let chan: Chan<Msg> = do on_osmain |port| {
|
impl CompositorChan {
|
||||||
debug!("preparing to enter main loop");
|
pub fn new(chan: Chan<Msg>) -> CompositorChan {
|
||||||
run_main_loop(port,
|
CompositorChan {
|
||||||
script_chan.take(),
|
|
||||||
shutdown_chan.take(),
|
|
||||||
profiler_chan.clone());
|
|
||||||
};
|
|
||||||
|
|
||||||
let task = CompositorTask {
|
|
||||||
chan: SharedChan::new(chan),
|
chan: SharedChan::new(chan),
|
||||||
};
|
}
|
||||||
(task, shutdown_port)
|
}
|
||||||
|
pub fn send(&self, msg: Msg) {
|
||||||
|
self.chan.send(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +84,8 @@ pub enum Msg {
|
||||||
ChangeReadyState(ReadyState),
|
ChangeReadyState(ReadyState),
|
||||||
/// Alerts the compositor to the current status of rendering.
|
/// Alerts the compositor to the current status of rendering.
|
||||||
ChangeRenderState(RenderState),
|
ChangeRenderState(RenderState),
|
||||||
|
/// Sets the channel to the current layout task
|
||||||
|
SetLayoutChan(LayoutChan),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Azure surface wrapping to work with the layers infrastructure.
|
/// Azure surface wrapping to work with the layers infrastructure.
|
||||||
|
@ -111,264 +113,279 @@ impl ImageData for AzureDrawTargetImageData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_main_loop(port: Port<Msg>,
|
pub struct CompositorTask {
|
||||||
script_chan: SharedChan<ScriptMsg>,
|
port: Port<Msg>,
|
||||||
shutdown_chan: Chan<()>,
|
profiler_chan: ProfilerChan,
|
||||||
profiler_chan: ProfilerChan) {
|
shutdown_chan: SharedChan<()>,
|
||||||
let app: Application = ApplicationMethods::new();
|
|
||||||
let window: @mut Window = WindowMethods::new(&app);
|
|
||||||
let resize_rate_limiter = @mut ResizeRateLimiter(script_chan.clone());
|
|
||||||
|
|
||||||
// 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 scene = @mut Scene(ContainerLayerKind(root_layer), Size2D(800.0, 600.0), identity());
|
|
||||||
let done = @mut 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(800, 600);
|
|
||||||
|
|
||||||
// Keeps track of the current zoom factor
|
|
||||||
let world_zoom = @mut 1f32;
|
|
||||||
|
|
||||||
let check_for_messages: @fn() = || {
|
|
||||||
// Periodically check if the script task responded to our last resize event
|
|
||||||
resize_rate_limiter.check_resize_response();
|
|
||||||
// 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),
|
|
||||||
|
|
||||||
GetGLContext(chan) => chan.send(current_gl_context()),
|
|
||||||
|
|
||||||
Paint(new_layer_buffer_set, new_size) => {
|
|
||||||
debug!("osmain: received new frame");
|
|
||||||
|
|
||||||
*page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
|
||||||
|
|
||||||
let mut new_layer_buffer_set = new_layer_buffer_set;
|
|
||||||
|
|
||||||
// Iterate over the children of the container layer.
|
|
||||||
let mut current_layer_child = root_layer.first_child;
|
|
||||||
|
|
||||||
// Replace the image layer data with the buffer data. Also compute the page
|
|
||||||
// size here.
|
|
||||||
let buffers = util::replace(&mut new_layer_buffer_set.buffers, ~[]);
|
|
||||||
|
|
||||||
for buffers.each |buffer| {
|
|
||||||
let width = buffer.rect.size.width as uint;
|
|
||||||
let height = buffer.rect.size.height as uint;
|
|
||||||
|
|
||||||
debug!("osmain: compositing buffer rect %?", &buffer.rect);
|
|
||||||
|
|
||||||
// Find or create a texture layer.
|
|
||||||
let texture_layer;
|
|
||||||
current_layer_child = match current_layer_child {
|
|
||||||
None => {
|
|
||||||
debug!("osmain: adding new texture layer");
|
|
||||||
texture_layer = @mut TextureLayer::new(@buffer.draw_target.clone() as @TextureManager,
|
|
||||||
buffer.rect.size);
|
|
||||||
root_layer.add_child(TextureLayerKind(texture_layer));
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Some(TextureLayerKind(existing_texture_layer)) => {
|
|
||||||
texture_layer = existing_texture_layer;
|
|
||||||
texture_layer.manager = @buffer.draw_target.clone() as @TextureManager;
|
|
||||||
|
|
||||||
// Move on to the next sibling.
|
|
||||||
do current_layer_child.get().with_common |common| {
|
|
||||||
common.next_sibling
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(_) => fail!(~"found unexpected layer kind"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let origin = buffer.screen_pos.origin;
|
|
||||||
let origin = Point2D(origin.x as f32, origin.y as f32);
|
|
||||||
|
|
||||||
// Set the layer's transform.
|
|
||||||
let transform = identity().translate(origin.x, origin.y, 0.0);
|
|
||||||
let transform = transform.scale(width as f32, height as f32, 1.0);
|
|
||||||
texture_layer.common.set_transform(transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Recycle the old buffers; send them back to the renderer to reuse if
|
|
||||||
// it wishes.
|
|
||||||
|
|
||||||
window.set_needs_display();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
do window.set_composite_callback {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.present();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hook the windowing system's resize callback up to the resize rate limiter.
|
|
||||||
do window.set_resize_callback |width, height| {
|
|
||||||
debug!("osmain: window resized to %ux%u", width, height);
|
|
||||||
*window_size = Size2D(width, height);
|
|
||||||
resize_rate_limiter.window_resized(width, height)
|
|
||||||
}
|
|
||||||
|
|
||||||
let script_chan_clone = script_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);
|
|
||||||
script_chan_clone.send(LoadMsg(url::make_url(url_string.to_str(), None)))
|
|
||||||
}
|
|
||||||
|
|
||||||
let script_chan_clone = script_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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
script_chan_clone.send(SendEventMsg(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
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);
|
|
||||||
|
|
||||||
debug!("compositor: scrolled to %?", *world_offset);
|
|
||||||
|
|
||||||
let mut scroll_transform = identity();
|
|
||||||
|
|
||||||
scroll_transform = scroll_transform.translate(window_size.width as f32 / 2f32 * *world_zoom - world_offset.x,
|
|
||||||
window_size.height as f32 / 2f32 * *world_zoom - world_offset.y,
|
|
||||||
0.0);
|
|
||||||
scroll_transform = scroll_transform.scale(*world_zoom, *world_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);
|
|
||||||
|
|
||||||
window.set_needs_display()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// When the user pinch-zooms, scale the layer
|
|
||||||
do window.set_zoom_callback |magnification| {
|
|
||||||
let old_world_zoom = *world_zoom;
|
|
||||||
|
|
||||||
// Determine zoom amount
|
|
||||||
*world_zoom = (*world_zoom * magnification).max(&1.0);
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
// Apply transformations
|
|
||||||
let mut zoom_transform = identity();
|
|
||||||
zoom_transform = zoom_transform.translate(window_size.width as f32 / 2f32 * *world_zoom - world_offset.x,
|
|
||||||
window_size.height as f32 / 2f32 * *world_zoom - world_offset.y,
|
|
||||||
0.0);
|
|
||||||
zoom_transform = zoom_transform.scale(*world_zoom, *world_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);
|
|
||||||
|
|
||||||
|
|
||||||
window.set_needs_display()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enter the main event loop.
|
|
||||||
while !*done {
|
|
||||||
// Check for new messages coming from the rendering task.
|
|
||||||
check_for_messages();
|
|
||||||
|
|
||||||
// Check for messages coming from the windowing system.
|
|
||||||
window.check_loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
shutdown_chan.send(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implementation of the abstract `Compositor` interface.
|
impl CompositorTask {
|
||||||
impl Compositor for CompositorTask {
|
pub fn new(port: Port<Msg>,
|
||||||
fn get_gl_context(&self) -> AzGLContext {
|
profiler_chan: ProfilerChan,
|
||||||
let (port, chan) = comm::stream();
|
shutdown_chan: Chan<()>)
|
||||||
self.chan.send(GetGLContext(chan));
|
-> CompositorTask {
|
||||||
port.recv()
|
CompositorTask {
|
||||||
|
port: port,
|
||||||
|
profiler_chan: profiler_chan,
|
||||||
|
shutdown_chan: SharedChan::new(shutdown_chan),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(&self, layer_buffer_set: LayerBufferSet, new_size: Size2D<uint>) {
|
/// Starts the compositor. Returns an interface that can be used to communicate with the
|
||||||
self.chan.send(Paint(layer_buffer_set, new_size))
|
/// compositor
|
||||||
|
pub fn create_compositor_task(port: Port<Msg>,
|
||||||
|
profiler_chan: ProfilerChan,
|
||||||
|
shutdown_chan: Chan<()>) {
|
||||||
|
let port = Cell(port);
|
||||||
|
let shutdown_chan = Cell(shutdown_chan);
|
||||||
|
do on_osmain {
|
||||||
|
let compositor_task = CompositorTask::new(port.take(),
|
||||||
|
profiler_chan.clone(),
|
||||||
|
shutdown_chan.take());
|
||||||
|
debug!("preparing to enter main loop");
|
||||||
|
compositor_task.run_main_loop();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
fn set_render_state(&self, render_state: RenderState) {
|
|
||||||
self.chan.send(ChangeRenderState(render_state))
|
fn run_main_loop(&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 scene = @mut Scene(ContainerLayerKind(root_layer), Size2D(800.0, 600.0), identity());
|
||||||
|
let done = @mut 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(800, 600);
|
||||||
|
|
||||||
|
// Keeps track of the current zoom factor
|
||||||
|
let world_zoom = @mut 1f32;
|
||||||
|
|
||||||
|
let update_layout_callbacks: @fn(LayoutChan) = |layout_chan: LayoutChan| {
|
||||||
|
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| {
|
||||||
|
debug!("osmain: window resized to %ux%u", width, height);
|
||||||
|
*window_size = Size2D(width, height);
|
||||||
|
layout_chan_clone.chan.send(RouteScriptMsg(SendEventMsg(ResizeEvent(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.chan.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.chan.send(RouteScriptMsg(SendEventMsg(event)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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),
|
||||||
|
|
||||||
|
SetLayoutChan(layout_chan) => {
|
||||||
|
update_layout_callbacks(layout_chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
GetGLContext(chan) => chan.send(current_gl_context()),
|
||||||
|
|
||||||
|
Paint(new_layer_buffer_set, new_size) => {
|
||||||
|
debug!("osmain: received new frame");
|
||||||
|
|
||||||
|
*page_size = Size2D(new_size.width as f32, new_size.height as f32);
|
||||||
|
|
||||||
|
let mut new_layer_buffer_set = new_layer_buffer_set;
|
||||||
|
|
||||||
|
// Iterate over the children of the container layer.
|
||||||
|
let mut current_layer_child = root_layer.first_child;
|
||||||
|
|
||||||
|
// Replace the image layer data with the buffer data. Also compute the page
|
||||||
|
// size here.
|
||||||
|
let buffers = util::replace(&mut new_layer_buffer_set.buffers, ~[]);
|
||||||
|
|
||||||
|
for buffers.each |buffer| {
|
||||||
|
let width = buffer.rect.size.width as uint;
|
||||||
|
let height = buffer.rect.size.height as uint;
|
||||||
|
|
||||||
|
debug!("osmain: compositing buffer rect %?", &buffer.rect);
|
||||||
|
|
||||||
|
// Find or create a texture layer.
|
||||||
|
let texture_layer;
|
||||||
|
current_layer_child = match current_layer_child {
|
||||||
|
None => {
|
||||||
|
debug!("osmain: adding new texture layer");
|
||||||
|
texture_layer = @mut TextureLayer::new(@buffer.draw_target.clone() as @TextureManager,
|
||||||
|
buffer.rect.size);
|
||||||
|
root_layer.add_child(TextureLayerKind(texture_layer));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
Some(TextureLayerKind(existing_texture_layer)) => {
|
||||||
|
texture_layer = existing_texture_layer;
|
||||||
|
texture_layer.manager = @buffer.draw_target.clone() as @TextureManager;
|
||||||
|
|
||||||
|
// Move on to the next sibling.
|
||||||
|
do current_layer_child.get().with_common |common| {
|
||||||
|
common.next_sibling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(_) => fail!(~"found unexpected layer kind"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let origin = buffer.screen_pos.origin;
|
||||||
|
let origin = Point2D(origin.x as f32, origin.y as f32);
|
||||||
|
|
||||||
|
// Set the layer's transform.
|
||||||
|
let transform = identity().translate(origin.x, origin.y, 0.0);
|
||||||
|
let transform = transform.scale(width as f32, height as f32, 1.0);
|
||||||
|
texture_layer.common.set_transform(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Recycle the old buffers; send them back to the renderer to reuse if
|
||||||
|
// it wishes.
|
||||||
|
|
||||||
|
window.set_needs_display();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let profiler_chan = self.profiler_chan.clone();
|
||||||
|
do window.set_composite_callback {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.present();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
debug!("compositor: scrolled to %?", *world_offset);
|
||||||
|
|
||||||
|
let mut scroll_transform = identity();
|
||||||
|
|
||||||
|
scroll_transform = scroll_transform.translate(window_size.width as f32 / 2f32 * *world_zoom - world_offset.x,
|
||||||
|
window_size.height as f32 / 2f32 * *world_zoom - world_offset.y,
|
||||||
|
0.0);
|
||||||
|
scroll_transform = scroll_transform.scale(*world_zoom, *world_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);
|
||||||
|
|
||||||
|
window.set_needs_display()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// When the user pinch-zooms, scale the layer
|
||||||
|
do window.set_zoom_callback |magnification| {
|
||||||
|
let old_world_zoom = *world_zoom;
|
||||||
|
|
||||||
|
// Determine zoom amount
|
||||||
|
*world_zoom = (*world_zoom * magnification).max(&1.0);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
// Apply transformations
|
||||||
|
let mut zoom_transform = identity();
|
||||||
|
zoom_transform = zoom_transform.translate(window_size.width as f32 / 2f32 * *world_zoom - world_offset.x,
|
||||||
|
window_size.height as f32 / 2f32 * *world_zoom - world_offset.y,
|
||||||
|
0.0);
|
||||||
|
zoom_transform = zoom_transform.scale(*world_zoom, *world_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);
|
||||||
|
|
||||||
|
|
||||||
|
window.set_needs_display()
|
||||||
|
}
|
||||||
|
// Enter the main event loop.
|
||||||
|
while !*done {
|
||||||
|
// Check for new messages coming from the rendering task.
|
||||||
|
check_for_messages(&self.port);
|
||||||
|
|
||||||
|
// Check for messages coming from the windowing system.
|
||||||
|
window.check_loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.shutdown_chan.send(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A function for spawning into the platform's main thread.
|
/// A function for spawning into the platform's main thread.
|
||||||
fn on_osmain<T: Owned>(f: ~fn(port: Port<T>)) -> Chan<T> {
|
fn on_osmain(f: ~fn()) {
|
||||||
let (setup_port, setup_chan) = comm::stream();
|
|
||||||
// FIXME: rust#6399
|
// FIXME: rust#6399
|
||||||
let mut main_task = task::task();
|
let mut main_task = task::task();
|
||||||
main_task.sched_mode(task::PlatformThread);
|
main_task.sched_mode(task::PlatformThread);
|
||||||
do main_task.spawn {
|
do main_task.spawn {
|
||||||
let (port, chan) = comm::stream();
|
f();
|
||||||
setup_chan.send(chan);
|
|
||||||
f(port);
|
|
||||||
}
|
}
|
||||||
setup_port.recv()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,20 +7,20 @@
|
||||||
/// before sending the next. If the window is resized multiple times before an event is handled
|
/// before sending the next. If the window is resized multiple times before an event is handled
|
||||||
/// then some events will never be sent.
|
/// then some events will never be sent.
|
||||||
|
|
||||||
use core::comm::{Port, SharedChan};
|
use core::comm::{Port};
|
||||||
use script::dom::event::ResizeEvent;
|
use script::dom::event::ResizeEvent;
|
||||||
use script::script_task::{ScriptMsg, SendEventMsg};
|
use script::script_task::{ScriptChan, ScriptMsg, SendEventMsg};
|
||||||
|
|
||||||
pub struct ResizeRateLimiter {
|
pub struct ResizeRateLimiter {
|
||||||
/// The channel we send resize events on
|
/// The channel we send resize events on
|
||||||
priv script_chan: SharedChan<ScriptMsg>,
|
priv script_chan: ScriptChan,
|
||||||
/// The port we are waiting on for a response to the last resize event
|
/// The port we are waiting on for a response to the last resize event
|
||||||
priv last_response_port: Option<Port<()>>,
|
priv last_response_port: Option<Port<()>>,
|
||||||
/// The next window resize event we should fire
|
/// The next window resize event we should fire
|
||||||
priv next_resize_event: Option<(uint, uint)>
|
priv next_resize_event: Option<(uint, uint)>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ResizeRateLimiter(script_chan: SharedChan<ScriptMsg>) -> ResizeRateLimiter {
|
pub fn ResizeRateLimiter(script_chan: ScriptChan) -> ResizeRateLimiter {
|
||||||
ResizeRateLimiter {
|
ResizeRateLimiter {
|
||||||
script_chan: script_chan,
|
script_chan: script_chan,
|
||||||
last_response_port: None,
|
last_response_port: None,
|
||||||
|
|
|
@ -2,101 +2,116 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use compositing::CompositorTask;
|
use compositing::{CompositorChan, SetLayoutChan};
|
||||||
use layout::layout_task;
|
use layout::layout_task;
|
||||||
|
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use core::comm::{Port, SharedChan};
|
use core::comm::Port;
|
||||||
use gfx::opts::Opts;
|
use gfx::opts::Opts;
|
||||||
use gfx::render_task::RenderTask;
|
use gfx::render_task::RenderChan;
|
||||||
use gfx::render_task;
|
use gfx::render_task;
|
||||||
use script::compositor_interface::{CompositorInterface, ReadyState};
|
use script::compositor_interface::{ScriptListener, ReadyState};
|
||||||
use script::engine_interface::{EngineTask, ExitMsg, LoadUrlMsg, Msg};
|
use script::engine_interface::{EngineChan, ExitMsg, LoadUrlMsg, Msg};
|
||||||
use script::layout_interface::LayoutTask;
|
use script::layout_interface::LayoutChan;
|
||||||
use script::layout_interface;
|
use script::layout_interface;
|
||||||
use script::script_task::{ExecuteMsg, LoadMsg, ScriptMsg, ScriptTask};
|
use script::script_task::{ExecuteMsg, LoadMsg, ScriptMsg, ScriptContext, ScriptChan};
|
||||||
use script::script_task;
|
use script::script_task;
|
||||||
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
|
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
|
||||||
use servo_net::resource_task::ResourceTask;
|
use servo_net::resource_task::ResourceTask;
|
||||||
use servo_net::resource_task;
|
use servo_net::resource_task;
|
||||||
use servo_util::time::{ProfilerChan, ProfilerPort, ProfilerTask, ForcePrintMsg};
|
use servo_util::time::{ProfilerChan, ForcePrintMsg};
|
||||||
|
|
||||||
pub struct Engine {
|
pub struct Engine {
|
||||||
request_port: Port<Msg>,
|
request_port: Port<Msg>,
|
||||||
compositor: CompositorTask,
|
compositor_chan: CompositorChan,
|
||||||
render_task: RenderTask,
|
render_chan: RenderChan,
|
||||||
resource_task: ResourceTask,
|
resource_task: ResourceTask,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
layout_task: LayoutTask,
|
layout_chan: LayoutChan,
|
||||||
script_task: ScriptTask,
|
script_chan: ScriptChan,
|
||||||
profiler_task: ProfilerTask,
|
profiler_chan: ProfilerChan,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Engine {
|
impl Drop for Engine {
|
||||||
fn finalize(&self) {
|
fn finalize(&self) {
|
||||||
self.profiler_task.chan.send(ForcePrintMsg);
|
self.profiler_chan.send(ForcePrintMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
pub fn start(compositor: CompositorTask,
|
pub fn start(compositor_chan: CompositorChan,
|
||||||
opts: &Opts,
|
opts: &Opts,
|
||||||
script_port: Port<ScriptMsg>,
|
|
||||||
script_chan: SharedChan<ScriptMsg>,
|
|
||||||
resource_task: ResourceTask,
|
resource_task: ResourceTask,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
profiler_port: ProfilerPort,
|
|
||||||
profiler_chan: ProfilerChan)
|
profiler_chan: ProfilerChan)
|
||||||
-> EngineTask {
|
-> EngineChan {
|
||||||
let (script_port, script_chan) = (Cell(script_port), Cell(script_chan));
|
macro_rules! closure_stream(
|
||||||
let (engine_port, engine_chan) = comm::stream();
|
($Msg:ty, $Chan:ident) => (
|
||||||
let (engine_port, engine_chan) = (Cell(engine_port), SharedChan::new(engine_chan));
|
{
|
||||||
let engine_chan_clone = engine_chan.clone();
|
let (port, chan) = comm::stream::<$Msg>();
|
||||||
let compositor = Cell(compositor);
|
(Cell(port), $Chan::new(chan))
|
||||||
let profiler_port = Cell(profiler_port);
|
}
|
||||||
|
);
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create the script port and channel.
|
||||||
|
let (script_port, script_chan) = closure_stream!(ScriptMsg, ScriptChan);
|
||||||
|
|
||||||
|
// Create the engine port and channel.
|
||||||
|
let (engine_port, engine_chan) = closure_stream!(Msg, EngineChan);
|
||||||
|
|
||||||
|
// Create the layout port and channel.
|
||||||
|
let (layout_port, layout_chan) = closure_stream!(layout_interface::Msg, LayoutChan);
|
||||||
|
|
||||||
|
let (render_port, render_chan) = closure_stream!(render_task::Msg, RenderChan);
|
||||||
|
|
||||||
|
compositor_chan.send(SetLayoutChan(layout_chan.clone()));
|
||||||
|
let compositor_chan = Cell(compositor_chan);
|
||||||
|
|
||||||
let opts = Cell(copy *opts);
|
let opts = Cell(copy *opts);
|
||||||
|
|
||||||
do task::spawn {
|
{
|
||||||
let compositor = compositor.take();
|
let engine_chan = engine_chan.clone();
|
||||||
let render_task = RenderTask::new(compositor.clone(),
|
do task::spawn {
|
||||||
opts.with_ref(|o| copy *o),
|
let compositor_chan = compositor_chan.take();
|
||||||
profiler_chan.clone());
|
render_task::create_render_task(render_port.take(),
|
||||||
|
compositor_chan.clone(),
|
||||||
|
opts.with_ref(|o| copy *o),
|
||||||
|
profiler_chan.clone());
|
||||||
|
|
||||||
let opts = opts.take();
|
let opts = opts.take();
|
||||||
|
|
||||||
let profiler_task = ProfilerTask::new(profiler_port.take(),
|
layout_task::create_layout_task(layout_port.take(),
|
||||||
profiler_chan.clone(),
|
script_chan.clone(),
|
||||||
opts.profiler_period);
|
render_chan.clone(),
|
||||||
|
image_cache_task.clone(),
|
||||||
|
opts,
|
||||||
|
profiler_chan.clone());
|
||||||
|
|
||||||
let layout_task = layout_task::create_layout_task(render_task.clone(),
|
let compositor_chan_clone = compositor_chan.clone();
|
||||||
image_cache_task.clone(),
|
ScriptContext::create_script_context(layout_chan.clone(),
|
||||||
opts,
|
script_port.take(),
|
||||||
profiler_task.chan.clone());
|
script_chan.clone(),
|
||||||
|
engine_chan.clone(),
|
||||||
|
|msg: ReadyState| {
|
||||||
|
compositor_chan_clone.set_ready_state(msg)
|
||||||
|
},
|
||||||
|
resource_task.clone(),
|
||||||
|
image_cache_task.clone());
|
||||||
|
|
||||||
let compositor_clone = compositor.clone();
|
Engine {
|
||||||
let script_task = ScriptTask::new(script_port.take(),
|
request_port: engine_port.take(),
|
||||||
script_chan.take(),
|
compositor_chan: compositor_chan.clone(),
|
||||||
engine_chan_clone.clone(),
|
render_chan: render_chan.clone(),
|
||||||
|msg: ReadyState| {
|
resource_task: resource_task.clone(),
|
||||||
compositor_clone.set_ready_state(msg)
|
image_cache_task: image_cache_task.clone(),
|
||||||
},
|
layout_chan: layout_chan.clone(),
|
||||||
layout_task.clone(),
|
script_chan: script_chan.clone(),
|
||||||
resource_task.clone(),
|
profiler_chan: profiler_chan.clone(),
|
||||||
image_cache_task.clone());
|
}.run();
|
||||||
|
}
|
||||||
|
|
||||||
Engine {
|
|
||||||
request_port: engine_port.take(),
|
|
||||||
compositor: compositor.clone(),
|
|
||||||
render_task: render_task,
|
|
||||||
resource_task: resource_task.clone(),
|
|
||||||
image_cache_task: image_cache_task.clone(),
|
|
||||||
layout_task: layout_task,
|
|
||||||
script_task: script_task,
|
|
||||||
profiler_task: profiler_task,
|
|
||||||
}.run();
|
|
||||||
}
|
}
|
||||||
engine_chan.clone()
|
engine_chan
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self) {
|
fn run(&self) {
|
||||||
|
@ -109,20 +124,20 @@ impl Engine {
|
||||||
match request {
|
match request {
|
||||||
LoadUrlMsg(url) => {
|
LoadUrlMsg(url) => {
|
||||||
if url.path.ends_with(".js") {
|
if url.path.ends_with(".js") {
|
||||||
self.script_task.chan.send(ExecuteMsg(url))
|
self.script_chan.send(ExecuteMsg(url))
|
||||||
} else {
|
} else {
|
||||||
self.script_task.chan.send(LoadMsg(url))
|
self.script_chan.send(LoadMsg(url))
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
ExitMsg(sender) => {
|
ExitMsg(sender) => {
|
||||||
self.script_task.chan.send(script_task::ExitMsg);
|
self.script_chan.send(script_task::ExitMsg);
|
||||||
self.layout_task.chan.send(layout_interface::ExitMsg);
|
self.layout_chan.send(layout_interface::ExitMsg);
|
||||||
|
|
||||||
let (response_port, response_chan) = comm::stream();
|
let (response_port, response_chan) = comm::stream();
|
||||||
|
|
||||||
self.render_task.channel.send(render_task::ExitMsg(response_chan));
|
self.render_chan.send(render_task::ExitMsg(response_chan));
|
||||||
response_port.recv();
|
response_port.recv();
|
||||||
|
|
||||||
self.image_cache_task.exit();
|
self.image_cache_task.exit();
|
||||||
|
|
|
@ -13,11 +13,10 @@ use layout::box_builder::LayoutTreeBuilder;
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods};
|
use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods};
|
||||||
use layout::flow::FlowContext;
|
use layout::flow::FlowContext;
|
||||||
use util::task::spawn_listener;
|
|
||||||
|
|
||||||
use core::cast::transmute;
|
use core::cast::transmute;
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use core::comm::{Chan, Port, SharedChan};
|
use core::comm::{Chan, Port};
|
||||||
use geom::point::Point2D;
|
use geom::point::Point2D;
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
|
@ -26,7 +25,7 @@ use gfx::font_context::FontContext;
|
||||||
use gfx::geometry::Au;
|
use gfx::geometry::Au;
|
||||||
use gfx::opts::Opts;
|
use gfx::opts::Opts;
|
||||||
use gfx::render_layers::RenderLayer;
|
use gfx::render_layers::RenderLayer;
|
||||||
use gfx::render_task::{RenderMsg, RenderTask};
|
use gfx::render_task::{RenderMsg, RenderChan};
|
||||||
use newcss::select::SelectCtx;
|
use newcss::select::SelectCtx;
|
||||||
use newcss::stylesheet::Stylesheet;
|
use newcss::stylesheet::Stylesheet;
|
||||||
use newcss::types::OriginAuthor;
|
use newcss::types::OriginAuthor;
|
||||||
|
@ -35,10 +34,10 @@ use script::dom::node::{AbstractNode, LayoutView};
|
||||||
use script::layout_interface::{AddStylesheetMsg, ContentBoxQuery};
|
use script::layout_interface::{AddStylesheetMsg, ContentBoxQuery};
|
||||||
use script::layout_interface::{HitTestQuery, ContentBoxResponse, HitTestResponse};
|
use script::layout_interface::{HitTestQuery, ContentBoxResponse, HitTestResponse};
|
||||||
use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery};
|
use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery};
|
||||||
use script::layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDocumentDamage, Msg};
|
use script::layout_interface::{LayoutResponse, MatchSelectorsDocumentDamage, Msg};
|
||||||
use script::layout_interface::{QueryMsg, Reflow, ReflowDocumentDamage, ReflowForDisplay};
|
use script::layout_interface::{QueryMsg, RouteScriptMsg, Reflow, ReflowDocumentDamage};
|
||||||
use script::layout_interface::{ReflowMsg};
|
use script::layout_interface::{ReflowForDisplay, ReflowMsg};
|
||||||
use script::script_task::{ReflowCompleteMsg, ScriptMsg, SendEventMsg};
|
use script::script_task::{ReflowCompleteMsg, ScriptChan, ScriptMsg, SendEventMsg};
|
||||||
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
|
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
|
||||||
use servo_net::local_image_cache::LocalImageCache;
|
use servo_net::local_image_cache::LocalImageCache;
|
||||||
use servo_util::tree::{TreeNodeRef, TreeUtils};
|
use servo_util::tree::{TreeNodeRef, TreeUtils};
|
||||||
|
@ -46,30 +45,30 @@ use servo_util::time::{ProfilerChan, profile, time};
|
||||||
use servo_util::time;
|
use servo_util::time;
|
||||||
use std::net::url::Url;
|
use std::net::url::Url;
|
||||||
|
|
||||||
pub fn create_layout_task(render_task: RenderTask,
|
pub fn create_layout_task(port: Port<Msg>,
|
||||||
|
script_chan: ScriptChan,
|
||||||
|
render_chan: RenderChan,
|
||||||
img_cache_task: ImageCacheTask,
|
img_cache_task: ImageCacheTask,
|
||||||
opts: Opts,
|
opts: Opts,
|
||||||
profiler_chan: ProfilerChan)
|
profiler_chan: ProfilerChan) {
|
||||||
-> LayoutTask {
|
let port = Cell(port);
|
||||||
let chan = do spawn_listener::<Msg> |from_script| {
|
do spawn {
|
||||||
let mut layout = Layout::new(render_task.clone(),
|
let mut layout = Layout::new(port.take(),
|
||||||
|
script_chan.clone(),
|
||||||
|
render_chan.clone(),
|
||||||
img_cache_task.clone(),
|
img_cache_task.clone(),
|
||||||
from_script,
|
|
||||||
&opts,
|
&opts,
|
||||||
profiler_chan.clone());
|
profiler_chan.clone());
|
||||||
layout.start();
|
layout.start();
|
||||||
};
|
};
|
||||||
|
|
||||||
LayoutTask {
|
|
||||||
chan: SharedChan::new(chan),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Layout {
|
struct Layout {
|
||||||
render_task: RenderTask,
|
port: Port<Msg>,
|
||||||
|
script_chan: ScriptChan,
|
||||||
|
render_chan: RenderChan,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
local_image_cache: @mut LocalImageCache,
|
local_image_cache: @mut LocalImageCache,
|
||||||
from_script: Port<Msg>,
|
|
||||||
font_ctx: @mut FontContext,
|
font_ctx: @mut FontContext,
|
||||||
doc_url: Option<Url>,
|
doc_url: Option<Url>,
|
||||||
screen_size: Option<Size2D<Au>>,
|
screen_size: Option<Size2D<Au>>,
|
||||||
|
@ -82,19 +81,21 @@ struct Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout {
|
impl Layout {
|
||||||
fn new(render_task: RenderTask,
|
fn new(port: Port<Msg>,
|
||||||
|
script_chan: ScriptChan,
|
||||||
|
render_chan: RenderChan,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
from_script: Port<Msg>,
|
|
||||||
opts: &Opts,
|
opts: &Opts,
|
||||||
profiler_chan: ProfilerChan)
|
profiler_chan: ProfilerChan)
|
||||||
-> Layout {
|
-> Layout {
|
||||||
let fctx = @mut FontContext::new(opts.render_backend, true, profiler_chan.clone());
|
let fctx = @mut FontContext::new(opts.render_backend, true, profiler_chan.clone());
|
||||||
|
|
||||||
Layout {
|
Layout {
|
||||||
render_task: render_task,
|
port: port,
|
||||||
|
script_chan: script_chan,
|
||||||
|
render_chan: render_chan,
|
||||||
image_cache_task: image_cache_task.clone(),
|
image_cache_task: image_cache_task.clone(),
|
||||||
local_image_cache: @mut LocalImageCache(image_cache_task),
|
local_image_cache: @mut LocalImageCache(image_cache_task),
|
||||||
from_script: from_script,
|
|
||||||
font_ctx: fctx,
|
font_ctx: fctx,
|
||||||
doc_url: None,
|
doc_url: None,
|
||||||
screen_size: None,
|
screen_size: None,
|
||||||
|
@ -125,7 +126,7 @@ impl Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_request(&mut self) -> bool {
|
fn handle_request(&mut self) -> bool {
|
||||||
match self.from_script.recv() {
|
match self.port.recv() {
|
||||||
AddStylesheetMsg(sheet) => self.handle_add_stylesheet(sheet),
|
AddStylesheetMsg(sheet) => self.handle_add_stylesheet(sheet),
|
||||||
ReflowMsg(data) => {
|
ReflowMsg(data) => {
|
||||||
let data = Cell(data);
|
let data = Cell(data);
|
||||||
|
@ -137,9 +138,12 @@ impl Layout {
|
||||||
QueryMsg(query, chan) => {
|
QueryMsg(query, chan) => {
|
||||||
let chan = Cell(chan);
|
let chan = Cell(chan);
|
||||||
do profile(time::LayoutQueryCategory, self.profiler_chan.clone()) {
|
do profile(time::LayoutQueryCategory, self.profiler_chan.clone()) {
|
||||||
self.handle_query(query, chan.take())
|
self.handle_query(query, chan.take());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RouteScriptMsg(script_msg) => {
|
||||||
|
self.route_script_msg(script_msg);
|
||||||
|
}
|
||||||
ExitMsg => {
|
ExitMsg => {
|
||||||
debug!("layout: ExitMsg received");
|
debug!("layout: ExitMsg received");
|
||||||
return false
|
return false
|
||||||
|
@ -248,7 +252,7 @@ impl Layout {
|
||||||
size: Size2D(root_size.width.to_px() as uint, root_size.height.to_px() as uint)
|
size: Size2D(root_size.width.to_px() as uint, root_size.height.to_px() as uint)
|
||||||
};
|
};
|
||||||
|
|
||||||
self.render_task.channel.send(RenderMsg(render_layer));
|
self.render_chan.send(RenderMsg(render_layer));
|
||||||
} // time(layout: display list building)
|
} // time(layout: display list building)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,12 +375,18 @@ impl Layout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(tkuehn): once there are multiple script tasks, this is where the layout task will
|
||||||
|
// determine which script task should receive the message. The prototype will need to change
|
||||||
|
fn route_script_msg(&self, script_msg: ScriptMsg) {
|
||||||
|
self.script_chan.send(script_msg);
|
||||||
|
}
|
||||||
|
|
||||||
// When images can't be loaded in time to display they trigger
|
// When images can't be loaded in time to display they trigger
|
||||||
// this callback in some task somewhere. This will send a message
|
// this callback in some task somewhere. This will send a message
|
||||||
// to the script task, and ultimately cause the image to be
|
// to the script task, and ultimately cause the image to be
|
||||||
// re-requested. We probably don't need to go all the way back to
|
// re-requested. We probably don't need to go all the way back to
|
||||||
// the script task for this.
|
// the script task for this.
|
||||||
fn make_on_image_available_cb(&self, script_chan: SharedChan<ScriptMsg>)
|
fn make_on_image_available_cb(&self, script_chan: ScriptChan)
|
||||||
-> @fn() -> ~fn(ImageResponseMsg) {
|
-> @fn() -> ~fn(ImageResponseMsg) {
|
||||||
// This has a crazy signature because the image cache needs to
|
// This has a crazy signature because the image cache needs to
|
||||||
// make multiple copies of the callback, and the dom event
|
// make multiple copies of the callback, and the dom event
|
||||||
|
|
|
@ -33,14 +33,14 @@ extern mod core_graphics;
|
||||||
#[cfg(target_os="macos")]
|
#[cfg(target_os="macos")]
|
||||||
extern mod core_text;
|
extern mod core_text;
|
||||||
|
|
||||||
use compositing::CompositorTask;
|
use compositing::{CompositorChan, CompositorTask};
|
||||||
use engine::Engine;
|
use engine::Engine;
|
||||||
use script::engine_interface::{ExitMsg, LoadUrlMsg};
|
use script::engine_interface::{ExitMsg, LoadUrlMsg};
|
||||||
|
|
||||||
use core::comm::SharedChan;
|
|
||||||
use gfx::opts;
|
use gfx::opts;
|
||||||
use servo_net::image_cache_task::ImageCacheTask;
|
use servo_net::image_cache_task::ImageCacheTask;
|
||||||
use servo_net::resource_task::ResourceTask;
|
use servo_net::resource_task::ResourceTask;
|
||||||
|
use servo_util::time::{Profiler, ProfilerChan};
|
||||||
|
|
||||||
pub use gfx::opts::Opts;
|
pub use gfx::opts::Opts;
|
||||||
pub use gfx::text;
|
pub use gfx::text;
|
||||||
|
@ -87,33 +87,31 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(opts: &Opts) {
|
fn run(opts: &Opts) {
|
||||||
// Create the script channel.
|
let (shutdown_port, shutdown_chan) = comm::stream();
|
||||||
let (script_port, script_chan) = comm::stream();
|
|
||||||
let script_chan = SharedChan::new(script_chan);
|
|
||||||
|
|
||||||
// Create the profiler channel.
|
// Create the profiler channel.
|
||||||
let (profiler_port, profiler_chan) = comm::stream();
|
let (profiler_port, profiler_chan) = comm::stream();
|
||||||
let profiler_chan = SharedChan::new(profiler_chan);
|
let profiler_chan = ProfilerChan::new(profiler_chan);
|
||||||
|
Profiler::create_profiler(profiler_port, opts.profiler_period);
|
||||||
|
|
||||||
// Create the compositor.
|
// Create the compositor.
|
||||||
let (compositor, shutdown_port) = CompositorTask::new(script_chan.clone(),
|
let (compositor_port, compositor_chan) = comm::stream();
|
||||||
profiler_chan.clone());
|
let compositor_chan = CompositorChan::new(compositor_chan);
|
||||||
|
CompositorTask::create_compositor_task(compositor_port, profiler_chan.clone(), shutdown_chan);
|
||||||
|
|
||||||
// Create a Servo instance.
|
// Create a Servo instance.
|
||||||
|
|
||||||
let resource_task = ResourceTask();
|
let resource_task = ResourceTask();
|
||||||
let image_cache_task = ImageCacheTask(resource_task.clone());
|
let image_cache_task = ImageCacheTask(resource_task.clone());
|
||||||
let engine_task = Engine::start(compositor.clone(),
|
let engine_chan = Engine::start(compositor_chan.clone(),
|
||||||
opts,
|
opts,
|
||||||
script_port,
|
|
||||||
script_chan,
|
|
||||||
resource_task,
|
resource_task,
|
||||||
image_cache_task,
|
image_cache_task,
|
||||||
profiler_port,
|
profiler_chan.clone());
|
||||||
profiler_chan);
|
|
||||||
|
|
||||||
// Send the URL command to the engine task.
|
// Send the URL command to the engine task.
|
||||||
for opts.urls.each |filename| {
|
for opts.urls.each |filename| {
|
||||||
engine_task.send(LoadUrlMsg(make_url(copy *filename, None)))
|
engine_chan.chan.send(LoadUrlMsg(make_url(copy *filename, None)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for the compositor to shut down.
|
// Wait for the compositor to shut down.
|
||||||
|
@ -122,7 +120,7 @@ fn run(opts: &Opts) {
|
||||||
// Shut the engine down.
|
// Shut the engine down.
|
||||||
debug!("master: Shut down");
|
debug!("master: Shut down");
|
||||||
let (exit_response_from_engine, exit_chan) = comm::stream();
|
let (exit_response_from_engine, exit_chan) = comm::stream();
|
||||||
engine_task.send(ExitMsg(exit_chan));
|
engine_chan.chan.send(ExitMsg(exit_chan));
|
||||||
exit_response_from_engine.recv();
|
exit_response_from_engine.recv();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,6 @@ pub enum ReadyState {
|
||||||
FinishedLoading,
|
FinishedLoading,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CompositorInterface : Clone {
|
pub trait ScriptListener : Clone {
|
||||||
fn set_ready_state(&self, ReadyState);
|
fn set_ready_state(&self, ReadyState);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use dom::bindings::utils::{DOMString, ErrorResult, WrapperCache};
|
||||||
use geom::point::Point2D;
|
use geom::point::Point2D;
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
ResizeEvent(uint, uint, comm::Chan<()>),
|
ResizeEvent(uint, uint),
|
||||||
ReflowEvent,
|
ReflowEvent,
|
||||||
ClickEvent(uint, Point2D<f32>),
|
ClickEvent(uint, Point2D<f32>),
|
||||||
MouseDownEvent(uint, Point2D<f32>),
|
MouseDownEvent(uint, Point2D<f32>),
|
||||||
|
|
|
@ -6,9 +6,9 @@ use dom::bindings::utils::WrapperCache;
|
||||||
use dom::bindings::window;
|
use dom::bindings::window;
|
||||||
|
|
||||||
use layout_interface::ReflowForScriptQuery;
|
use layout_interface::ReflowForScriptQuery;
|
||||||
use script_task::{ExitMsg, FireTimerMsg, ScriptMsg, ScriptContext};
|
use script_task::{ExitMsg, FireTimerMsg, ScriptChan, ScriptContext};
|
||||||
|
|
||||||
use core::comm::{Chan, SharedChan};
|
use core::comm::Chan;
|
||||||
use js::jsapi::JSVal;
|
use js::jsapi::JSVal;
|
||||||
use std::timer;
|
use std::timer;
|
||||||
use std::uv_global_loop;
|
use std::uv_global_loop;
|
||||||
|
@ -23,7 +23,7 @@ pub enum TimerControlMsg {
|
||||||
// only used for querying layout from arbitrary script.
|
// only used for querying layout from arbitrary script.
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
timer_chan: Chan<TimerControlMsg>,
|
timer_chan: Chan<TimerControlMsg>,
|
||||||
script_chan: SharedChan<ScriptMsg>,
|
script_chan: ScriptChan,
|
||||||
script_context: *mut ScriptContext,
|
script_context: *mut ScriptContext,
|
||||||
wrapper: WrapperCache
|
wrapper: WrapperCache
|
||||||
}
|
}
|
||||||
|
@ -88,9 +88,9 @@ pub impl Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(script_chan: SharedChan<ScriptMsg>, script_context: *mut ScriptContext)
|
pub fn new(script_chan: ScriptChan, script_context: *mut ScriptContext)
|
||||||
-> @mut Window {
|
-> @mut Window {
|
||||||
let script_chan_copy = script_chan.clone();
|
let script_chan_clone = script_chan.clone();
|
||||||
let win = @mut Window {
|
let win = @mut Window {
|
||||||
wrapper: WrapperCache::new(),
|
wrapper: WrapperCache::new(),
|
||||||
script_chan: script_chan,
|
script_chan: script_chan,
|
||||||
|
@ -100,8 +100,8 @@ pub impl Window {
|
||||||
loop {
|
loop {
|
||||||
match timer_port.recv() {
|
match timer_port.recv() {
|
||||||
TimerMessage_Close => break,
|
TimerMessage_Close => break,
|
||||||
TimerMessage_Fire(td) => script_chan_copy.send(FireTimerMsg(td)),
|
TimerMessage_Fire(td) => script_chan_clone.chan.send(FireTimerMsg(td)),
|
||||||
TimerMessage_TriggerExit => script_chan_copy.send(ExitMsg),
|
TimerMessage_TriggerExit => script_chan_clone.chan.send(ExitMsg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,21 @@
|
||||||
use core::comm::{Chan, SharedChan};
|
use core::comm::{Chan, SharedChan};
|
||||||
use std::net::url::Url;
|
use std::net::url::Url;
|
||||||
|
|
||||||
pub type EngineTask = SharedChan<Msg>;
|
#[deriving(Clone)]
|
||||||
|
pub struct EngineChan {
|
||||||
|
chan: SharedChan<Msg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EngineChan {
|
||||||
|
pub fn new(chan: Chan<Msg>) -> EngineChan {
|
||||||
|
EngineChan {
|
||||||
|
chan: SharedChan::new(chan),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn send(&self, msg: Msg) {
|
||||||
|
self.chan.send(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
LoadUrlMsg(Url),
|
LoadUrlMsg(Url),
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
/// from layout.
|
/// from layout.
|
||||||
|
|
||||||
use dom::node::{AbstractNode, ScriptView, LayoutView};
|
use dom::node::{AbstractNode, ScriptView, LayoutView};
|
||||||
use script_task::ScriptMsg;
|
use script_task::{ScriptMsg, ScriptChan};
|
||||||
|
|
||||||
use core::comm::{Chan, SharedChan};
|
use core::comm::{Chan, SharedChan};
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
|
@ -32,6 +32,9 @@ pub enum Msg {
|
||||||
/// FIXME(pcwalton): As noted below, this isn't very type safe.
|
/// FIXME(pcwalton): As noted below, this isn't very type safe.
|
||||||
QueryMsg(LayoutQuery, Chan<Result<LayoutResponse,()>>),
|
QueryMsg(LayoutQuery, Chan<Result<LayoutResponse,()>>),
|
||||||
|
|
||||||
|
/// Routes a message (usually from the compositor) to the appropriate script task
|
||||||
|
RouteScriptMsg(ScriptMsg),
|
||||||
|
|
||||||
/// Requests that the layout task shut down and exit.
|
/// Requests that the layout task shut down and exit.
|
||||||
ExitMsg,
|
ExitMsg,
|
||||||
}
|
}
|
||||||
|
@ -110,7 +113,7 @@ pub struct Reflow {
|
||||||
/// The URL of the page.
|
/// The URL of the page.
|
||||||
url: Url,
|
url: Url,
|
||||||
/// The channel through which messages can be sent back to the script task.
|
/// The channel through which messages can be sent back to the script task.
|
||||||
script_chan: SharedChan<ScriptMsg>,
|
script_chan: ScriptChan,
|
||||||
/// The current window size.
|
/// The current window size.
|
||||||
window_size: Size2D<uint>,
|
window_size: Size2D<uint>,
|
||||||
/// The channel that we send a notification to.
|
/// The channel that we send a notification to.
|
||||||
|
@ -119,7 +122,17 @@ pub struct Reflow {
|
||||||
|
|
||||||
/// Encapsulates a channel to the layout task.
|
/// Encapsulates a channel to the layout task.
|
||||||
#[deriving(Clone)]
|
#[deriving(Clone)]
|
||||||
pub struct LayoutTask {
|
pub struct LayoutChan {
|
||||||
chan: SharedChan<Msg>,
|
chan: SharedChan<Msg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl LayoutChan {
|
||||||
|
pub fn new(chan: Chan<Msg>) -> LayoutChan {
|
||||||
|
LayoutChan {
|
||||||
|
chan: SharedChan::new(chan),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn send(&self, msg: Msg) {
|
||||||
|
self.chan.send(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -13,11 +13,11 @@ use dom::event::{Event, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent, Mo
|
||||||
use dom::node::{AbstractNode, ScriptView, define_bindings};
|
use dom::node::{AbstractNode, ScriptView, define_bindings};
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use layout_interface::{AddStylesheetMsg, DocumentDamage, DocumentDamageLevel, HitTestQuery};
|
use layout_interface::{AddStylesheetMsg, DocumentDamage, DocumentDamageLevel, HitTestQuery};
|
||||||
use layout_interface::{HitTestResponse, LayoutQuery, LayoutResponse, LayoutTask};
|
use layout_interface::{HitTestResponse, LayoutQuery, LayoutResponse, LayoutChan};
|
||||||
use layout_interface::{MatchSelectorsDocumentDamage, QueryMsg, Reflow, ReflowDocumentDamage};
|
use layout_interface::{MatchSelectorsDocumentDamage, QueryMsg, Reflow, ReflowDocumentDamage};
|
||||||
use layout_interface::{ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg};
|
use layout_interface::{ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg};
|
||||||
use layout_interface;
|
use layout_interface;
|
||||||
use engine_interface::{EngineTask, LoadUrlMsg};
|
use engine_interface::{EngineChan, LoadUrlMsg};
|
||||||
|
|
||||||
use core::cast::transmute;
|
use core::cast::transmute;
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
|
@ -61,42 +61,22 @@ pub enum ScriptMsg {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encapsulates external communication with the script task.
|
/// Encapsulates external communication with the script task.
|
||||||
pub struct ScriptTask {
|
#[deriving(Clone)]
|
||||||
|
pub struct ScriptChan {
|
||||||
/// The channel used to send messages to the script task.
|
/// The channel used to send messages to the script task.
|
||||||
chan: SharedChan<ScriptMsg>,
|
chan: SharedChan<ScriptMsg>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScriptTask {
|
impl ScriptChan {
|
||||||
/// Creates a new script task.
|
/// Creates a new script task.
|
||||||
pub fn new(script_port: Port<ScriptMsg>,
|
pub fn new(chan: Chan<ScriptMsg>) -> ScriptChan {
|
||||||
script_chan: SharedChan<ScriptMsg>,
|
ScriptChan {
|
||||||
engine_task: EngineTask,
|
chan: SharedChan::new(chan)
|
||||||
//FIXME(rust #5192): workaround for lack of working ~Trait
|
|
||||||
compositor_task: ~fn(ReadyState),
|
|
||||||
layout_task: LayoutTask,
|
|
||||||
resource_task: ResourceTask,
|
|
||||||
image_cache_task: ImageCacheTask)
|
|
||||||
-> ScriptTask {
|
|
||||||
let (script_chan_copy, script_port) = (script_chan.clone(), Cell(script_port));
|
|
||||||
let compositor_task = Cell(compositor_task);
|
|
||||||
// FIXME: rust#6399
|
|
||||||
let mut the_task = task();
|
|
||||||
the_task.sched_mode(SingleThreaded);
|
|
||||||
do the_task.spawn {
|
|
||||||
let script_context = ScriptContext::new(layout_task.clone(),
|
|
||||||
script_port.take(),
|
|
||||||
script_chan_copy.clone(),
|
|
||||||
engine_task.clone(),
|
|
||||||
compositor_task.take(),
|
|
||||||
resource_task.clone(),
|
|
||||||
image_cache_task.clone());
|
|
||||||
script_context.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
ScriptTask {
|
|
||||||
chan: script_chan
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn send(&self, msg: ScriptMsg) {
|
||||||
|
self.chan.send(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information for one frame in the browsing context.
|
/// Information for one frame in the browsing context.
|
||||||
|
@ -112,7 +92,7 @@ pub struct Frame {
|
||||||
/// FIXME: Rename to `Page`, following WebKit?
|
/// FIXME: Rename to `Page`, following WebKit?
|
||||||
pub struct ScriptContext {
|
pub struct ScriptContext {
|
||||||
/// A handle to the layout task.
|
/// A handle to the layout task.
|
||||||
layout_task: LayoutTask,
|
layout_chan: LayoutChan,
|
||||||
/// A handle to the image cache task.
|
/// A handle to the image cache task.
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
/// A handle to the resource task.
|
/// A handle to the resource task.
|
||||||
|
@ -125,10 +105,10 @@ pub struct ScriptContext {
|
||||||
script_port: Port<ScriptMsg>,
|
script_port: Port<ScriptMsg>,
|
||||||
/// A channel for us to hand out when we want some other task to be able to send us script
|
/// A channel for us to hand out when we want some other task to be able to send us script
|
||||||
/// messages.
|
/// messages.
|
||||||
script_chan: SharedChan<ScriptMsg>,
|
script_chan: ScriptChan,
|
||||||
|
|
||||||
/// For communicating load url messages to the engine
|
/// For communicating load url messages to the engine
|
||||||
engine_task: EngineTask,
|
engine_chan: EngineChan,
|
||||||
/// For communicating loading messages to the compositor
|
/// For communicating loading messages to the compositor
|
||||||
compositor_task: ~fn(ReadyState),
|
compositor_task: ~fn(ReadyState),
|
||||||
|
|
||||||
|
@ -180,10 +160,10 @@ impl Drop for ScriptContext {
|
||||||
|
|
||||||
impl ScriptContext {
|
impl ScriptContext {
|
||||||
/// Creates a new script context.
|
/// Creates a new script context.
|
||||||
pub fn new(layout_task: LayoutTask,
|
pub fn new(layout_chan: LayoutChan,
|
||||||
script_port: Port<ScriptMsg>,
|
script_port: Port<ScriptMsg>,
|
||||||
script_chan: SharedChan<ScriptMsg>,
|
script_chan: ScriptChan,
|
||||||
engine_task: EngineTask,
|
engine_chan: EngineChan,
|
||||||
compositor_task: ~fn(ReadyState),
|
compositor_task: ~fn(ReadyState),
|
||||||
resource_task: ResourceTask,
|
resource_task: ResourceTask,
|
||||||
img_cache_task: ImageCacheTask)
|
img_cache_task: ImageCacheTask)
|
||||||
|
@ -200,7 +180,7 @@ impl ScriptContext {
|
||||||
};
|
};
|
||||||
|
|
||||||
let script_context = @mut ScriptContext {
|
let script_context = @mut ScriptContext {
|
||||||
layout_task: layout_task,
|
layout_chan: layout_chan,
|
||||||
image_cache_task: img_cache_task,
|
image_cache_task: img_cache_task,
|
||||||
resource_task: resource_task,
|
resource_task: resource_task,
|
||||||
|
|
||||||
|
@ -208,7 +188,7 @@ impl ScriptContext {
|
||||||
script_port: script_port,
|
script_port: script_port,
|
||||||
script_chan: script_chan,
|
script_chan: script_chan,
|
||||||
|
|
||||||
engine_task: engine_task,
|
engine_chan: engine_chan,
|
||||||
compositor_task: compositor_task,
|
compositor_task: compositor_task,
|
||||||
|
|
||||||
js_runtime: js_runtime,
|
js_runtime: js_runtime,
|
||||||
|
@ -245,6 +225,30 @@ impl ScriptContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_script_context(layout_chan: LayoutChan,
|
||||||
|
script_port: Port<ScriptMsg>,
|
||||||
|
script_chan: ScriptChan,
|
||||||
|
engine_chan: EngineChan,
|
||||||
|
compositor_task: ~fn(ReadyState),
|
||||||
|
resource_task: ResourceTask,
|
||||||
|
image_cache_task: ImageCacheTask) {
|
||||||
|
let script_port = Cell(script_port);
|
||||||
|
let compositor_task = Cell(compositor_task);
|
||||||
|
// FIXME: rust#6399
|
||||||
|
let mut the_task = task();
|
||||||
|
the_task.sched_mode(SingleThreaded);
|
||||||
|
do the_task.spawn {
|
||||||
|
let script_context = ScriptContext::new(layout_chan.clone(),
|
||||||
|
script_port.take(),
|
||||||
|
script_chan.clone(),
|
||||||
|
engine_chan.clone(),
|
||||||
|
compositor_task.take(),
|
||||||
|
resource_task.clone(),
|
||||||
|
image_cache_task.clone());
|
||||||
|
script_context.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Handles an incoming control message.
|
/// Handles an incoming control message.
|
||||||
fn handle_msg(&mut self) -> bool {
|
fn handle_msg(&mut self) -> bool {
|
||||||
match self.script_port.recv() {
|
match self.script_port.recv() {
|
||||||
|
@ -325,7 +329,7 @@ impl ScriptContext {
|
||||||
frame.document.teardown();
|
frame.document.teardown();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.layout_task.chan.send(layout_interface::ExitMsg)
|
self.layout_chan.send(layout_interface::ExitMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tells the compositor when loading starts and finishes
|
// tells the compositor when loading starts and finishes
|
||||||
|
@ -361,7 +365,7 @@ impl ScriptContext {
|
||||||
// in the script task.
|
// in the script task.
|
||||||
loop {
|
loop {
|
||||||
match html_parsing_result.style_port.recv() {
|
match html_parsing_result.style_port.recv() {
|
||||||
Some(sheet) => self.layout_task.chan.send(AddStylesheetMsg(sheet)),
|
Some(sheet) => self.layout_chan.send(AddStylesheetMsg(sheet)),
|
||||||
None => break,
|
None => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -457,7 +461,7 @@ impl ScriptContext {
|
||||||
damage: replace(&mut self.damage, None).unwrap(),
|
damage: replace(&mut self.damage, None).unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.layout_task.chan.send(ReflowMsg(reflow))
|
self.layout_chan.send(ReflowMsg(reflow))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,7 +486,7 @@ impl ScriptContext {
|
||||||
self.join_layout();
|
self.join_layout();
|
||||||
|
|
||||||
let (response_port, response_chan) = comm::stream();
|
let (response_port, response_chan) = comm::stream();
|
||||||
self.layout_task.chan.send(QueryMsg(query, response_chan));
|
self.layout_chan.send(QueryMsg(query, response_chan));
|
||||||
response_port.recv()
|
response_port.recv()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,7 +515,7 @@ impl ScriptContext {
|
||||||
/// TODO: Actually perform DOM event dispatch.
|
/// TODO: Actually perform DOM event dispatch.
|
||||||
fn handle_event(&mut self, event: Event) {
|
fn handle_event(&mut self, event: Event) {
|
||||||
match event {
|
match event {
|
||||||
ResizeEvent(new_width, new_height, response_chan) => {
|
ResizeEvent(new_width, new_height) => {
|
||||||
debug!("script got resize event: %u, %u", new_width, new_height);
|
debug!("script got resize event: %u, %u", new_width, new_height);
|
||||||
|
|
||||||
self.window_size = Size2D(new_width, new_height);
|
self.window_size = Size2D(new_width, new_height);
|
||||||
|
@ -525,8 +529,6 @@ impl ScriptContext {
|
||||||
if self.root_frame.is_some() {
|
if self.root_frame.is_some() {
|
||||||
self.reflow(ReflowForDisplay)
|
self.reflow(ReflowForDisplay)
|
||||||
}
|
}
|
||||||
|
|
||||||
response_chan.send(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(pcwalton): This reflows the entire document and is not incremental-y.
|
// FIXME(pcwalton): This reflows the entire document and is not incremental-y.
|
||||||
|
@ -595,7 +597,7 @@ impl ScriptContext {
|
||||||
None => None
|
None => None
|
||||||
};
|
};
|
||||||
let url = make_url(attr.value.clone(), current_url);
|
let url = make_url(attr.value.clone(), current_url);
|
||||||
self.engine_task.send(LoadUrlMsg(url));
|
self.engine_chan.send(LoadUrlMsg(url));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,22 @@ use core::cell::Cell;
|
||||||
use core::comm::{Port, SharedChan};
|
use core::comm::{Port, SharedChan};
|
||||||
use std::sort::tim_sort;
|
use std::sort::tim_sort;
|
||||||
|
|
||||||
pub type ProfilerChan = SharedChan<ProfilerMsg>;
|
// front-end representation of the profiler used to communicate with the profiler
|
||||||
pub type ProfilerPort = Port<ProfilerMsg>;
|
#[deriving(Clone)]
|
||||||
|
pub struct ProfilerChan {
|
||||||
|
chan: SharedChan<ProfilerMsg>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProfilerChan {
|
||||||
|
pub fn new(chan: Chan<ProfilerMsg>) -> ProfilerChan {
|
||||||
|
ProfilerChan {
|
||||||
|
chan: SharedChan::new(chan),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn send(&self, msg: ProfilerMsg) {
|
||||||
|
self.chan.send(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[deriving(Eq)]
|
#[deriving(Eq)]
|
||||||
pub enum ProfilerCategory {
|
pub enum ProfilerCategory {
|
||||||
|
@ -39,14 +53,9 @@ pub enum ProfilerMsg {
|
||||||
ForcePrintMsg,
|
ForcePrintMsg,
|
||||||
}
|
}
|
||||||
|
|
||||||
// front-end representation of the profiler used to communicate with the profiler context
|
|
||||||
pub struct ProfilerTask {
|
|
||||||
chan: ProfilerChan,
|
|
||||||
}
|
|
||||||
|
|
||||||
// back end of the profiler that handles data aggregation and performance metrics
|
// back end of the profiler that handles data aggregation and performance metrics
|
||||||
pub struct ProfilerContext {
|
pub struct Profiler {
|
||||||
port: ProfilerPort,
|
port: Port<ProfilerMsg>,
|
||||||
buckets: ~[(ProfilerCategory, ~[f64])],
|
buckets: ~[(ProfilerCategory, ~[f64])],
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
period: f64,
|
period: f64,
|
||||||
|
@ -54,7 +63,6 @@ pub struct ProfilerContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProfilerCategory {
|
impl ProfilerCategory {
|
||||||
|
|
||||||
// convenience function to not have to cast every time
|
// convenience function to not have to cast every time
|
||||||
pub fn num_buckets() -> uint {
|
pub fn num_buckets() -> uint {
|
||||||
NUM_BUCKETS as uint
|
NUM_BUCKETS as uint
|
||||||
|
@ -103,31 +111,21 @@ impl ProfilerCategory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProfilerTask {
|
impl Profiler {
|
||||||
pub fn new(profiler_port: ProfilerPort,
|
pub fn create_profiler(port: Port<ProfilerMsg>, period: Option<f64>) {
|
||||||
profiler_chan: ProfilerChan,
|
let port = Cell(port);
|
||||||
period: Option<f64>)
|
|
||||||
-> ProfilerTask {
|
|
||||||
let profiler_port = Cell(profiler_port);
|
|
||||||
|
|
||||||
do spawn {
|
do spawn {
|
||||||
let mut profiler_context = ProfilerContext::new(profiler_port.take(), period);
|
let mut profiler = Profiler::new(port.take(), period);
|
||||||
profiler_context.start();
|
profiler.start();
|
||||||
}
|
|
||||||
|
|
||||||
ProfilerTask {
|
|
||||||
chan: profiler_chan
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl ProfilerContext {
|
pub fn new(port: Port<ProfilerMsg>, period: Option<f64>) -> Profiler {
|
||||||
pub fn new(port: ProfilerPort, period: Option<f64>) -> ProfilerContext {
|
|
||||||
let (verbose, period) = match period {
|
let (verbose, period) = match period {
|
||||||
Some(period) => (true, period),
|
Some(period) => (true, period),
|
||||||
None => (false, 0f64)
|
None => (false, 0f64)
|
||||||
};
|
};
|
||||||
ProfilerContext {
|
Profiler {
|
||||||
port: port,
|
port: port,
|
||||||
buckets: ProfilerCategory::empty_buckets(),
|
buckets: ProfilerCategory::empty_buckets(),
|
||||||
verbose: verbose,
|
verbose: verbose,
|
||||||
|
@ -200,7 +198,7 @@ pub fn profile<T>(category: ProfilerCategory,
|
||||||
let val = callback();
|
let val = callback();
|
||||||
let end_time = precise_time_ns();
|
let end_time = precise_time_ns();
|
||||||
let ms = ((end_time - start_time) as f64 / 1000000f64);
|
let ms = ((end_time - start_time) as f64 / 1000000f64);
|
||||||
profiler_chan.send(TimeMsg(category, ms));
|
profiler_chan.chan.send(TimeMsg(category, ms));
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 865f539114383a021822583801e8362faf916699
|
Subproject commit 09d2db847c11bcab7f1832d5daf5947a7c1384ee
|
|
@ -1 +1 @@
|
||||||
Subproject commit 453bf81e021008f5eba29b135f07f4529e6c8b2e
|
Subproject commit 6f6b6fa95914fa6322f3277c803fd4921601cb90
|
|
@ -1 +1 @@
|
||||||
Subproject commit da248d3f5b3ed6d9e804c543563be8e34baf1673
|
Subproject commit d722188de3876ed748382965eb4f300fc1b78bf8
|
Loading…
Add table
Add a link
Reference in a new issue