mirror of
https://github.com/servo/servo.git
synced 2025-07-29 02:00:23 +01:00
Invert control flow, fix resizing, and improve checkerboarding
significantly by giving tiles some time to paint before we render unrendered content.
This commit is contained in:
parent
e483a189a3
commit
10f7b49cf7
27 changed files with 1195 additions and 678 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -221,14 +221,14 @@ name = "glfw"
|
|||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/glfw-rs?ref=servo#a15c2d04b8969aea653841d1d79e5fdf68de664b"
|
||||
dependencies = [
|
||||
"glfw-sys 3.0.4 (git+https://github.com/servo/glfw?ref=cargo-3.0.4#65a2b4721276589d9de24f6a9999a2db37286cae)",
|
||||
"glfw-sys 3.0.4 (git+https://github.com/servo/glfw?ref=cargo-3.0.4#aa8e0d26cccdb4145f34c5a1724d7e48e0399674)",
|
||||
"semver 0.0.1 (git+https://github.com/rust-lang/semver#d04583a173395b76c1eaa15cc630a5f6f8f0ae10)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glfw-sys"
|
||||
version = "3.0.4"
|
||||
source = "git+https://github.com/servo/glfw?ref=cargo-3.0.4#65a2b4721276589d9de24f6a9999a2db37286cae"
|
||||
source = "git+https://github.com/servo/glfw?ref=cargo-3.0.4#aa8e0d26cccdb4145f34c5a1724d7e48e0399674"
|
||||
|
||||
[[package]]
|
||||
name = "glfw_app"
|
||||
|
|
|
@ -7,21 +7,23 @@ use compositor_layer::{ScrollPositionChanged, WantsScrollEvents};
|
|||
use compositor_task::{Msg, CompositorTask, Exit, ChangeReadyState, SetIds, LayerProperties};
|
||||
use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpdateDescendantLayer};
|
||||
use compositor_task::{SetLayerOrigin, Paint, ScrollFragmentPoint, LoadComplete};
|
||||
use compositor_task::{ShutdownComplete, ChangeRenderState, RenderMsgDiscarded};
|
||||
use compositor_task::{ShutdownComplete, ChangeRenderState, RenderMsgDiscarded, ScrollTimeout};
|
||||
use compositor_task::{CompositorEventListener, CompositorProxy, CompositorReceiver};
|
||||
use constellation::SendableFrameTree;
|
||||
use pipeline::CompositionPipeline;
|
||||
use scrolling::ScrollingTimerProxy;
|
||||
use windowing;
|
||||
use windowing::{FinishedWindowEvent, IdleWindowEvent, LoadUrlWindowEvent, MouseWindowClickEvent};
|
||||
use windowing::{MouseWindowEvent, MouseWindowEventClass, MouseWindowMouseDownEvent};
|
||||
use windowing::{MouseWindowMouseUpEvent, MouseWindowMoveEventClass, NavigationWindowEvent};
|
||||
use windowing::{QuitWindowEvent, RefreshWindowEvent, ResizeWindowEvent, ScrollWindowEvent};
|
||||
use windowing::{WindowEvent, WindowMethods, WindowNavigateMsg, ZoomWindowEvent};
|
||||
use windowing::PinchZoomWindowEvent;
|
||||
use windowing::{PinchZoomWindowEvent};
|
||||
|
||||
use azure::azure_hl;
|
||||
use std::cmp;
|
||||
use std::mem;
|
||||
use std::num::Zero;
|
||||
use std::time::duration::Duration;
|
||||
use geom::point::{Point2D, TypedPoint2D};
|
||||
use geom::rect::{Rect, TypedRect};
|
||||
use geom::size::TypedSize2D;
|
||||
|
@ -45,20 +47,18 @@ use servo_util::memory::MemoryProfilerChan;
|
|||
use servo_util::opts;
|
||||
use servo_util::time::{profile, TimeProfilerChan};
|
||||
use servo_util::{memory, time};
|
||||
use std::io::timer::sleep;
|
||||
use std::collections::hashmap::HashMap;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
use time::precise_time_s;
|
||||
use time::{precise_time_ns, precise_time_s};
|
||||
use url::Url;
|
||||
|
||||
|
||||
pub struct IOCompositor<Window: WindowMethods> {
|
||||
/// The application window.
|
||||
window: Rc<Window>,
|
||||
|
||||
/// The port on which we receive messages.
|
||||
port: Receiver<Msg>,
|
||||
port: Box<CompositorReceiver>,
|
||||
|
||||
/// The render context.
|
||||
context: RenderContext,
|
||||
|
@ -82,20 +82,23 @@ pub struct IOCompositor<Window: WindowMethods> {
|
|||
/// The device pixel ratio for this window.
|
||||
hidpi_factor: ScaleFactor<ScreenPx, DevicePixel, f32>,
|
||||
|
||||
/// Tracks whether the renderer has finished its first rendering
|
||||
composite_ready: bool,
|
||||
/// A handle to the scrolling timer.
|
||||
scrolling_timer: ScrollingTimerProxy,
|
||||
|
||||
/// Tracks whether we should composite this frame.
|
||||
composition_request: CompositionRequest,
|
||||
|
||||
/// Tracks whether we are in the process of shutting down, or have shut down and should close
|
||||
/// the compositor.
|
||||
shutdown_state: ShutdownState,
|
||||
|
||||
/// Tracks whether we need to re-composite a page.
|
||||
recomposite: bool,
|
||||
|
||||
/// Tracks outstanding render_msg's sent to the render tasks.
|
||||
outstanding_render_msgs: uint,
|
||||
|
||||
/// Tracks whether the zoom action has happend recently.
|
||||
/// Tracks the last composite time.
|
||||
last_composite_time: u64,
|
||||
|
||||
/// Tracks whether the zoom action has happened recently.
|
||||
zoom_action: bool,
|
||||
|
||||
/// The time of the last zoom action has started.
|
||||
|
@ -112,6 +115,9 @@ pub struct IOCompositor<Window: WindowMethods> {
|
|||
/// many times for a single page.
|
||||
got_load_complete_message: bool,
|
||||
|
||||
/// Whether we have gotten a `SetIds` message.
|
||||
got_set_ids_message: bool,
|
||||
|
||||
/// The channel on which messages can be sent to the constellation.
|
||||
constellation_chan: ConstellationChan,
|
||||
|
||||
|
@ -122,10 +128,25 @@ pub struct IOCompositor<Window: WindowMethods> {
|
|||
memory_profiler_chan: MemoryProfilerChan,
|
||||
|
||||
/// Pending scroll to fragment event, if any
|
||||
fragment_point: Option<Point2D<f32>>
|
||||
fragment_point: Option<Point2D<f32>>,
|
||||
|
||||
/// Pending scroll events.
|
||||
pending_scroll_events: Vec<ScrollEvent>,
|
||||
}
|
||||
|
||||
pub struct ScrollEvent {
|
||||
delta: TypedPoint2D<DevicePixel,f32>,
|
||||
cursor: TypedPoint2D<DevicePixel,i32>,
|
||||
}
|
||||
|
||||
#[deriving(PartialEq)]
|
||||
enum CompositionRequest {
|
||||
NoCompositingNecessary,
|
||||
CompositeOnScrollTimeout(u64),
|
||||
CompositeNow,
|
||||
}
|
||||
|
||||
#[deriving(PartialEq, Show)]
|
||||
enum ShutdownState {
|
||||
NotShuttingDown,
|
||||
ShuttingDown,
|
||||
|
@ -139,11 +160,12 @@ struct HitTestResult {
|
|||
|
||||
impl<Window: WindowMethods> IOCompositor<Window> {
|
||||
fn new(window: Rc<Window>,
|
||||
port: Receiver<Msg>,
|
||||
sender: Box<CompositorProxy+Send>,
|
||||
receiver: Box<CompositorReceiver>,
|
||||
constellation_chan: ConstellationChan,
|
||||
time_profiler_chan: TimeProfilerChan,
|
||||
memory_profiler_chan: MemoryProfilerChan) -> IOCompositor<Window> {
|
||||
|
||||
memory_profiler_chan: MemoryProfilerChan)
|
||||
-> IOCompositor<Window> {
|
||||
// Create an initial layer tree.
|
||||
//
|
||||
// TODO: There should be no initial layer tree until the renderer creates one from the
|
||||
|
@ -155,7 +177,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
let show_debug_borders = opts::get().show_debug_borders;
|
||||
IOCompositor {
|
||||
window: window,
|
||||
port: port,
|
||||
port: receiver,
|
||||
context: rendergl::RenderContext::new(context, show_debug_borders),
|
||||
root_pipeline: None,
|
||||
scene: Scene::new(Rect {
|
||||
|
@ -164,9 +186,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
}),
|
||||
window_size: window_size,
|
||||
hidpi_factor: hidpi_factor,
|
||||
composite_ready: false,
|
||||
scrolling_timer: ScrollingTimerProxy::new(sender),
|
||||
composition_request: NoCompositingNecessary,
|
||||
pending_scroll_events: Vec::new(),
|
||||
shutdown_state: NotShuttingDown,
|
||||
recomposite: false,
|
||||
page_zoom: ScaleFactor(1.0),
|
||||
viewport_zoom: ScaleFactor(1.0),
|
||||
zoom_action: false,
|
||||
|
@ -174,99 +197,45 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
ready_states: HashMap::new(),
|
||||
render_states: HashMap::new(),
|
||||
got_load_complete_message: false,
|
||||
got_set_ids_message: false,
|
||||
constellation_chan: constellation_chan,
|
||||
time_profiler_chan: time_profiler_chan,
|
||||
memory_profiler_chan: memory_profiler_chan,
|
||||
fragment_point: None,
|
||||
outstanding_render_msgs: 0,
|
||||
last_composite_time: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(window: Rc<Window>,
|
||||
port: Receiver<Msg>,
|
||||
sender: Box<CompositorProxy+Send>,
|
||||
receiver: Box<CompositorReceiver>,
|
||||
constellation_chan: ConstellationChan,
|
||||
time_profiler_chan: TimeProfilerChan,
|
||||
memory_profiler_chan: MemoryProfilerChan) {
|
||||
memory_profiler_chan: MemoryProfilerChan)
|
||||
-> IOCompositor<Window> {
|
||||
let mut compositor = IOCompositor::new(window,
|
||||
port,
|
||||
sender,
|
||||
receiver,
|
||||
constellation_chan,
|
||||
time_profiler_chan,
|
||||
memory_profiler_chan);
|
||||
|
||||
// Set the size of the root layer.
|
||||
compositor.update_zoom_transform();
|
||||
|
||||
// Starts the compositor, which listens for messages on the specified port.
|
||||
compositor.run();
|
||||
}
|
||||
|
||||
fn run (&mut self) {
|
||||
// Tell the constellation about the initial window size.
|
||||
self.send_window_size();
|
||||
compositor.send_window_size();
|
||||
|
||||
// Enter the main event loop.
|
||||
while self.shutdown_state != FinishedShuttingDown {
|
||||
// Check for new messages coming from the rendering task.
|
||||
self.handle_message();
|
||||
|
||||
if self.shutdown_state == FinishedShuttingDown {
|
||||
// We have exited the compositor and passing window
|
||||
// messages to script may crash.
|
||||
debug!("Exiting the compositor due to a request from script.");
|
||||
break;
|
||||
compositor
|
||||
}
|
||||
|
||||
// Check for messages coming from the windowing system.
|
||||
let msg = self.window.recv();
|
||||
self.handle_window_message(msg);
|
||||
|
||||
// If asked to recomposite and renderer has run at least once
|
||||
if self.recomposite && self.composite_ready {
|
||||
self.recomposite = false;
|
||||
self.composite();
|
||||
}
|
||||
|
||||
sleep(Duration::milliseconds(10));
|
||||
|
||||
// If a pinch-zoom happened recently, ask for tiles at the new resolution
|
||||
if self.zoom_action && precise_time_s() - self.zoom_time > 0.3 {
|
||||
self.zoom_action = false;
|
||||
self.scene.mark_layer_contents_as_changed_recursively();
|
||||
self.send_buffer_requests_for_all_layers();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Clear out the compositor layers so that painting tasks can destroy the buffers.
|
||||
match self.scene.root {
|
||||
None => {}
|
||||
Some(ref layer) => layer.forget_all_tiles(),
|
||||
}
|
||||
|
||||
// Drain compositor port, sometimes messages contain channels that are blocking
|
||||
// another task from finishing (i.e. SetIds)
|
||||
loop {
|
||||
match self.port.try_recv() {
|
||||
Err(_) => break,
|
||||
Ok(_) => {},
|
||||
}
|
||||
}
|
||||
|
||||
// Tell the profiler and memory profiler to shut down.
|
||||
let TimeProfilerChan(ref time_profiler_chan) = self.time_profiler_chan;
|
||||
time_profiler_chan.send(time::ExitMsg);
|
||||
|
||||
let MemoryProfilerChan(ref memory_profiler_chan) = self.memory_profiler_chan;
|
||||
memory_profiler_chan.send(memory::ExitMsg);
|
||||
}
|
||||
|
||||
fn handle_message(&mut self) {
|
||||
loop {
|
||||
match (self.port.try_recv(), self.shutdown_state) {
|
||||
fn handle_browser_message(&mut self, msg: Msg) -> bool {
|
||||
match (msg, self.shutdown_state) {
|
||||
(_, FinishedShuttingDown) =>
|
||||
fail!("compositor shouldn't be handling messages after shutting down"),
|
||||
|
||||
(Err(_), _) => break,
|
||||
|
||||
(Ok(Exit(chan)), _) => {
|
||||
(Exit(chan), _) => {
|
||||
debug!("shutting down the constellation");
|
||||
let ConstellationChan(ref con_chan) = self.constellation_chan;
|
||||
con_chan.send(ExitMsg);
|
||||
|
@ -274,59 +243,74 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
self.shutdown_state = ShuttingDown;
|
||||
}
|
||||
|
||||
(Ok(ShutdownComplete), _) => {
|
||||
(ShutdownComplete, _) => {
|
||||
debug!("constellation completed shutdown");
|
||||
self.shutdown_state = FinishedShuttingDown;
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
(Ok(ChangeReadyState(pipeline_id, ready_state)), NotShuttingDown) => {
|
||||
(ChangeReadyState(pipeline_id, ready_state), NotShuttingDown) => {
|
||||
self.change_ready_state(pipeline_id, ready_state);
|
||||
}
|
||||
|
||||
(Ok(ChangeRenderState(pipeline_id, render_state)), NotShuttingDown) => {
|
||||
(ChangeRenderState(pipeline_id, render_state), NotShuttingDown) => {
|
||||
self.change_render_state(pipeline_id, render_state);
|
||||
}
|
||||
|
||||
(Ok(RenderMsgDiscarded), NotShuttingDown) => {
|
||||
(RenderMsgDiscarded, NotShuttingDown) => {
|
||||
self.remove_outstanding_render_msg();
|
||||
}
|
||||
|
||||
(Ok(SetIds(frame_tree, response_chan, new_constellation_chan)), _) => {
|
||||
(SetIds(frame_tree, response_chan, new_constellation_chan), NotShuttingDown) => {
|
||||
self.set_frame_tree(&frame_tree,
|
||||
response_chan,
|
||||
new_constellation_chan);
|
||||
}
|
||||
|
||||
(Ok(GetGraphicsMetadata(chan)), NotShuttingDown) => {
|
||||
chan.send(Some(self.window.native_metadata()));
|
||||
}
|
||||
|
||||
(Ok(CreateOrUpdateRootLayer(layer_properties)), NotShuttingDown) => {
|
||||
(CreateOrUpdateRootLayer(layer_properties), NotShuttingDown) => {
|
||||
self.create_or_update_root_layer(layer_properties);
|
||||
}
|
||||
|
||||
(Ok(CreateOrUpdateDescendantLayer(layer_properties)), NotShuttingDown) => {
|
||||
(CreateOrUpdateDescendantLayer(layer_properties), NotShuttingDown) => {
|
||||
self.create_or_update_descendant_layer(layer_properties);
|
||||
}
|
||||
|
||||
(Ok(SetLayerOrigin(pipeline_id, layer_id, origin)), NotShuttingDown) => {
|
||||
(GetGraphicsMetadata(chan), NotShuttingDown) => {
|
||||
chan.send(Some(self.window.native_metadata()));
|
||||
}
|
||||
|
||||
(SetLayerOrigin(pipeline_id, layer_id, origin), NotShuttingDown) => {
|
||||
self.set_layer_origin(pipeline_id, layer_id, origin);
|
||||
}
|
||||
|
||||
(Ok(Paint(pipeline_id, epoch, replies)), NotShuttingDown) => {
|
||||
(Paint(pipeline_id, epoch, replies), NotShuttingDown) => {
|
||||
for (layer_id, new_layer_buffer_set) in replies.into_iter() {
|
||||
self.paint(pipeline_id, layer_id, new_layer_buffer_set, epoch);
|
||||
}
|
||||
self.remove_outstanding_render_msg();
|
||||
}
|
||||
|
||||
(Ok(ScrollFragmentPoint(pipeline_id, layer_id, point)), NotShuttingDown) => {
|
||||
(ScrollFragmentPoint(pipeline_id, layer_id, point), NotShuttingDown) => {
|
||||
self.scroll_fragment_to_point(pipeline_id, layer_id, point);
|
||||
}
|
||||
|
||||
(Ok(LoadComplete(..)), NotShuttingDown) => {
|
||||
(LoadComplete(..), NotShuttingDown) => {
|
||||
self.got_load_complete_message = true;
|
||||
|
||||
// If we're rendering in headless mode, schedule a recomposite.
|
||||
if opts::get().output_file.is_some() {
|
||||
self.composite_if_necessary();
|
||||
}
|
||||
}
|
||||
|
||||
(ScrollTimeout(timestamp), NotShuttingDown) => {
|
||||
debug!("scroll timeout, drawing unrendered content!");
|
||||
match self.composition_request {
|
||||
CompositeOnScrollTimeout(this_timestamp) if timestamp == this_timestamp => {
|
||||
self.composition_request = CompositeNow
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
// When we are shutting_down, we need to avoid performing operations
|
||||
|
@ -334,7 +318,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
// the rest of our resources.
|
||||
(_, ShuttingDown) => { }
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn change_ready_state(&mut self, pipeline_id: PipelineId, ready_state: ReadyState) {
|
||||
|
@ -342,6 +327,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
ready_state,
|
||||
|_key, value| *value = ready_state);
|
||||
self.window.set_ready_state(self.get_earliest_pipeline_ready_state());
|
||||
|
||||
// If we're rendering in headless mode, schedule a recomposite.
|
||||
if opts::get().output_file.is_some() {
|
||||
self.composite_if_necessary()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_earliest_pipeline_ready_state(&self) -> ReadyState {
|
||||
|
@ -357,9 +347,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
render_state,
|
||||
|_key, value| *value = render_state);
|
||||
self.window.set_render_state(render_state);
|
||||
if render_state == IdleRenderState {
|
||||
self.composite_ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn all_pipelines_in_idle_render_state(&self) -> bool {
|
||||
|
@ -417,6 +404,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
// Initialize the new constellation channel by sending it the root window size.
|
||||
self.constellation_chan = new_constellation_chan;
|
||||
self.send_window_size();
|
||||
|
||||
self.got_set_ids_message = true;
|
||||
self.composite_if_necessary();
|
||||
}
|
||||
|
||||
fn create_frame_tree_root_layers(&mut self,
|
||||
|
@ -529,7 +519,6 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
}));
|
||||
}
|
||||
|
||||
|
||||
pub fn move_layer(&self,
|
||||
pipeline_id: PipelineId,
|
||||
layer_id: LayerId,
|
||||
|
@ -555,10 +544,21 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
fail!("Compositor: Tried to scroll to fragment with unknown layer.");
|
||||
}
|
||||
|
||||
self.recomposite = true;
|
||||
self.start_scrolling_timer_if_necessary();
|
||||
}
|
||||
None => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn start_scrolling_timer_if_necessary(&mut self) {
|
||||
match self.composition_request {
|
||||
CompositeNow | CompositeOnScrollTimeout(_) => return,
|
||||
NoCompositingNecessary => {}
|
||||
}
|
||||
|
||||
let timestamp = precise_time_ns();
|
||||
self.scrolling_timer.scroll_event_processed(timestamp);
|
||||
self.composition_request = CompositeOnScrollTimeout(timestamp);
|
||||
}
|
||||
|
||||
fn set_layer_origin(&mut self,
|
||||
|
@ -580,7 +580,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
layer_id: LayerId,
|
||||
new_layer_buffer_set: Box<LayerBufferSet>,
|
||||
epoch: Epoch) {
|
||||
debug!("compositor received new frame");
|
||||
debug!("compositor received new frame at size {}x{}",
|
||||
self.window_size.width.get(),
|
||||
self.window_size.height.get());
|
||||
|
||||
// From now on, if we destroy the buffers, they will leak.
|
||||
let mut new_layer_buffer_set = new_layer_buffer_set;
|
||||
|
@ -588,8 +590,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
|
||||
match self.find_layer_with_pipeline_and_layer_id(pipeline_id, layer_id) {
|
||||
Some(ref layer) => {
|
||||
// FIXME(pcwalton): This is going to cause problems with inconsistent frames since
|
||||
// we only composite one layer at a time.
|
||||
assert!(layer.add_buffers(new_layer_buffer_set, epoch));
|
||||
self.recomposite = true;
|
||||
self.composite_if_necessary();
|
||||
}
|
||||
None => {
|
||||
// FIXME: This may potentially be triggered by a race condition where a
|
||||
|
@ -605,8 +609,9 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
layer_id: LayerId,
|
||||
point: Point2D<f32>) {
|
||||
if self.move_layer(pipeline_id, layer_id, Point2D::from_untyped(&point)) {
|
||||
self.recomposite = true;
|
||||
self.send_buffer_requests_for_all_layers();
|
||||
if self.send_buffer_requests_for_all_layers() {
|
||||
self.start_scrolling_timer_if_necessary();
|
||||
}
|
||||
} else {
|
||||
self.fragment_point = Some(point);
|
||||
}
|
||||
|
@ -617,7 +622,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
IdleWindowEvent => {}
|
||||
|
||||
RefreshWindowEvent => {
|
||||
self.recomposite = true;
|
||||
self.composite_if_necessary()
|
||||
}
|
||||
|
||||
ResizeWindowEvent(size) => {
|
||||
|
@ -685,6 +690,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
|
||||
debug!("osmain: window resized to {:?}", new_size);
|
||||
self.window_size = new_size;
|
||||
|
||||
self.scene.set_root_layer_size(new_size.as_f32());
|
||||
self.send_window_size();
|
||||
}
|
||||
|
@ -698,7 +704,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
layers"),
|
||||
};
|
||||
|
||||
let msg = LoadUrlMsg(root_pipeline_id, LoadData::new(Url::parse(url_string.as_slice()).unwrap()));
|
||||
let msg = LoadUrlMsg(root_pipeline_id,
|
||||
LoadData::new(Url::parse(url_string.as_slice()).unwrap()));
|
||||
let ConstellationChan(ref chan) = self.constellation_chan;
|
||||
chan.send(msg);
|
||||
}
|
||||
|
@ -725,19 +732,30 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
fn on_scroll_window_event(&mut self,
|
||||
delta: TypedPoint2D<DevicePixel, f32>,
|
||||
cursor: TypedPoint2D<DevicePixel, i32>) {
|
||||
let delta = delta / self.scene.scale;
|
||||
let cursor = cursor.as_f32() / self.scene.scale;
|
||||
self.pending_scroll_events.push(ScrollEvent {
|
||||
delta: delta,
|
||||
cursor: cursor,
|
||||
});
|
||||
|
||||
let mut scroll = false;
|
||||
match self.scene.root {
|
||||
self.composite_if_necessary();
|
||||
}
|
||||
|
||||
fn process_pending_scroll_events(&mut self) {
|
||||
for scroll_event in mem::replace(&mut self.pending_scroll_events, Vec::new()).into_iter() {
|
||||
let delta = scroll_event.delta / self.scene.scale;
|
||||
let cursor = scroll_event.cursor.as_f32() / self.scene.scale;
|
||||
|
||||
let scrolled = match self.scene.root {
|
||||
Some(ref mut layer) => {
|
||||
scroll = layer.handle_scroll_event(delta, cursor) == ScrollPositionChanged;
|
||||
layer.handle_scroll_event(delta, cursor) == ScrollPositionChanged
|
||||
}
|
||||
None => { }
|
||||
}
|
||||
self.recomposite_if(scroll);
|
||||
None => false,
|
||||
};
|
||||
|
||||
self.start_scrolling_timer_if_necessary();
|
||||
self.send_buffer_requests_for_all_layers();
|
||||
}
|
||||
}
|
||||
|
||||
fn device_pixels_per_screen_px(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
|
||||
match opts::get().device_pixels_per_px {
|
||||
|
@ -768,6 +786,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
self.send_window_size();
|
||||
}
|
||||
|
||||
// TODO(pcwalton): I think this should go through the same queuing as scroll events do.
|
||||
fn on_pinch_zoom_window_event(&mut self, magnification: f32) {
|
||||
self.zoom_action = true;
|
||||
self.zoom_time = precise_time_s();
|
||||
|
@ -792,7 +811,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
None => { }
|
||||
}
|
||||
|
||||
self.recomposite = true;
|
||||
self.composite_if_necessary();
|
||||
}
|
||||
|
||||
fn on_navigation_window_event(&self, direction: WindowNavigateMsg) {
|
||||
|
@ -840,9 +859,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
match self.root_pipeline {
|
||||
Some(ref pipeline) => {
|
||||
let unused_buffers = self.scene.collect_unused_buffers();
|
||||
let have_unused_buffers = unused_buffers.len() > 0;
|
||||
self.recomposite = self.recomposite || have_unused_buffers;
|
||||
if have_unused_buffers {
|
||||
if unused_buffers.len() != 0 {
|
||||
let message = UnusedBufferMsg(unused_buffers);
|
||||
let _ = pipeline.render_chan.send_opt(message);
|
||||
}
|
||||
|
@ -851,7 +868,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
}
|
||||
}
|
||||
|
||||
fn send_buffer_requests_for_all_layers(&mut self) {
|
||||
/// Returns true if any buffer requests were sent or false otherwise.
|
||||
fn send_buffer_requests_for_all_layers(&mut self) -> bool {
|
||||
let mut layers_and_requests = Vec::new();
|
||||
self.scene.get_buffer_requests(&mut layers_and_requests,
|
||||
Rect(TypedPoint2D(0f32, 0f32), self.window_size.as_f32()));
|
||||
|
@ -860,7 +878,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
self.send_back_unused_buffers();
|
||||
|
||||
if layers_and_requests.len() == 0 {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// We want to batch requests for each pipeline to avoid race conditions
|
||||
|
@ -875,6 +893,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
}
|
||||
|
||||
self.add_outstanding_render_msg(num_render_msgs_sent);
|
||||
true
|
||||
}
|
||||
|
||||
fn is_ready_to_render_image_output(&self) -> bool {
|
||||
|
@ -893,6 +912,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
if !self.all_pipelines_in_idle_render_state() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !self.got_set_ids_message {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -939,10 +963,11 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
});
|
||||
|
||||
if output_image {
|
||||
let path = from_str::<Path>(opts::get().output_file.as_ref().unwrap().as_slice()).unwrap();
|
||||
let path =
|
||||
from_str::<Path>(opts::get().output_file.as_ref().unwrap().as_slice()).unwrap();
|
||||
let mut pixels = gl::read_pixels(0, 0,
|
||||
width as GLsizei,
|
||||
height as GLsizei,
|
||||
width as gl::GLsizei,
|
||||
height as gl::GLsizei,
|
||||
gl::RGB, gl::UNSIGNED_BYTE);
|
||||
|
||||
gl::bind_framebuffer(gl::FRAMEBUFFER, 0);
|
||||
|
@ -976,18 +1001,26 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
|||
self.shutdown_state = ShuttingDown;
|
||||
}
|
||||
|
||||
// Perform the page flip. This will likely block for a while.
|
||||
self.window.present();
|
||||
|
||||
self.last_composite_time = precise_time_ns();
|
||||
|
||||
let exit = opts::get().exit_after_load;
|
||||
if exit {
|
||||
debug!("shutting down the constellation for exit_after_load");
|
||||
let ConstellationChan(ref chan) = self.constellation_chan;
|
||||
chan.send(ExitMsg);
|
||||
}
|
||||
|
||||
self.composition_request = NoCompositingNecessary;
|
||||
self.process_pending_scroll_events();
|
||||
}
|
||||
|
||||
fn recomposite_if(&mut self, result: bool) {
|
||||
self.recomposite = result || self.recomposite;
|
||||
fn composite_if_necessary(&mut self) {
|
||||
if self.composition_request == NoCompositingNecessary {
|
||||
self.composition_request = CompositeNow
|
||||
}
|
||||
}
|
||||
|
||||
fn find_topmost_layer_at_point_for_layer(&self,
|
||||
|
@ -1054,3 +1087,87 @@ fn find_layer_with_pipeline_and_layer_id_for_layer(layer: Rc<Layer<CompositorDat
|
|||
|
||||
return None;
|
||||
}
|
||||
|
||||
impl<Window> CompositorEventListener for IOCompositor<Window> where Window: WindowMethods {
|
||||
fn handle_event(&mut self, msg: WindowEvent) -> bool {
|
||||
// Check for new messages coming from the other tasks in the system.
|
||||
loop {
|
||||
match self.port.try_recv_compositor_msg() {
|
||||
None => break,
|
||||
Some(msg) => {
|
||||
if !self.handle_browser_message(msg) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.shutdown_state == FinishedShuttingDown {
|
||||
// We have exited the compositor and passing window
|
||||
// messages to script may crash.
|
||||
debug!("Exiting the compositor due to a request from script.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle the message coming from the windowing system.
|
||||
self.handle_window_message(msg);
|
||||
|
||||
// If a pinch-zoom happened recently, ask for tiles at the new resolution
|
||||
if self.zoom_action && precise_time_s() - self.zoom_time > 0.3 {
|
||||
self.zoom_action = false;
|
||||
self.scene.mark_layer_contents_as_changed_recursively();
|
||||
self.send_buffer_requests_for_all_layers();
|
||||
}
|
||||
|
||||
match self.composition_request {
|
||||
NoCompositingNecessary | CompositeOnScrollTimeout(_) => {}
|
||||
CompositeNow => self.composite(),
|
||||
}
|
||||
|
||||
self.shutdown_state != FinishedShuttingDown
|
||||
}
|
||||
|
||||
/// Repaints and recomposites synchronously. You must be careful when calling this, as if a
|
||||
/// paint is not scheduled the compositor will hang forever.
|
||||
///
|
||||
/// This is used when resizing the window.
|
||||
fn repaint_synchronously(&mut self) {
|
||||
while self.shutdown_state != ShuttingDown {
|
||||
let msg = self.port.recv_compositor_msg();
|
||||
let is_paint = match msg {
|
||||
Paint(..) => true,
|
||||
_ => false,
|
||||
};
|
||||
let keep_going = self.handle_browser_message(msg);
|
||||
if is_paint {
|
||||
self.composite();
|
||||
break
|
||||
}
|
||||
if !keep_going {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn shutdown(&mut self) {
|
||||
// Clear out the compositor layers so that painting tasks can destroy the buffers.
|
||||
match self.scene.root {
|
||||
None => {}
|
||||
Some(ref layer) => layer.forget_all_tiles(),
|
||||
}
|
||||
|
||||
// Drain compositor port, sometimes messages contain channels that are blocking
|
||||
// another task from finishing (i.e. SetIds)
|
||||
while self.port.try_recv_compositor_msg().is_some() {}
|
||||
|
||||
// Tell the profiler, memory profiler, and scrolling timer to shut down.
|
||||
let TimeProfilerChan(ref time_profiler_chan) = self.time_profiler_chan;
|
||||
time_profiler_chan.send(time::ExitMsg);
|
||||
|
||||
let MemoryProfilerChan(ref memory_profiler_chan) = self.memory_profiler_chan;
|
||||
memory_profiler_chan.send(memory::ExitMsg);
|
||||
|
||||
self.scrolling_timer.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
* 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/. */
|
||||
|
||||
//! Communication with the compositor task.
|
||||
|
||||
pub use windowing;
|
||||
pub use constellation::SendableFrameTree;
|
||||
|
||||
use compositor;
|
||||
use headless;
|
||||
pub use constellation::SendableFrameTree;
|
||||
use windowing::WindowMethods;
|
||||
use windowing::{WindowEvent, WindowMethods};
|
||||
|
||||
use azure::azure_hl::{SourceSurfaceMethods, Color};
|
||||
use geom::point::Point2D;
|
||||
|
@ -21,39 +23,64 @@ use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
|
|||
use servo_util::memory::MemoryProfilerChan;
|
||||
use servo_util::time::TimeProfilerChan;
|
||||
use std::comm::{channel, Sender, Receiver};
|
||||
use std::fmt::{FormatError, Formatter, Show};
|
||||
use std::rc::Rc;
|
||||
|
||||
use url::Url;
|
||||
|
||||
/// The implementation of the layers-based compositor.
|
||||
#[deriving(Clone)]
|
||||
pub struct CompositorChan {
|
||||
/// A channel on which messages can be sent to the compositor.
|
||||
pub chan: Sender<Msg>,
|
||||
/// Sends messages to the compositor. This is a trait supplied by the port because the method used
|
||||
/// to communicate with the compositor may have to kick OS event loops awake, communicate cross-
|
||||
/// process, and so forth.
|
||||
pub trait CompositorProxy : 'static + Send {
|
||||
/// Sends a message to the compositor.
|
||||
fn send(&mut self, msg: Msg);
|
||||
/// Clones the compositor proxy.
|
||||
fn clone_compositor_proxy(&self) -> Box<CompositorProxy+'static+Send>;
|
||||
}
|
||||
|
||||
/// The port that the compositor receives messages on. As above, this is a trait supplied by the
|
||||
/// Servo port.
|
||||
pub trait CompositorReceiver for Sized? : 'static {
|
||||
/// Receives the next message inbound for the compositor. This must not block.
|
||||
fn try_recv_compositor_msg(&mut self) -> Option<Msg>;
|
||||
/// Synchronously waits for, and returns, the next message inbound for the compositor.
|
||||
fn recv_compositor_msg(&mut self) -> Msg;
|
||||
}
|
||||
|
||||
/// A convenience implementation of `CompositorReceiver` for a plain old Rust `Receiver`.
|
||||
impl CompositorReceiver for Receiver<Msg> {
|
||||
fn try_recv_compositor_msg(&mut self) -> Option<Msg> {
|
||||
match self.try_recv() {
|
||||
Ok(msg) => Some(msg),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
fn recv_compositor_msg(&mut self) -> Msg {
|
||||
self.recv()
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of the abstract `ScriptListener` interface.
|
||||
impl ScriptListener for CompositorChan {
|
||||
fn set_ready_state(&self, pipeline_id: PipelineId, ready_state: ReadyState) {
|
||||
impl ScriptListener for Box<CompositorProxy+'static+Send> {
|
||||
fn set_ready_state(&mut self, pipeline_id: PipelineId, ready_state: ReadyState) {
|
||||
let msg = ChangeReadyState(pipeline_id, ready_state);
|
||||
self.chan.send(msg);
|
||||
self.send(msg);
|
||||
}
|
||||
|
||||
fn scroll_fragment_point(&self,
|
||||
fn scroll_fragment_point(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
layer_id: LayerId,
|
||||
point: Point2D<f32>) {
|
||||
self.chan.send(ScrollFragmentPoint(pipeline_id, layer_id, point));
|
||||
self.send(ScrollFragmentPoint(pipeline_id, layer_id, point));
|
||||
}
|
||||
|
||||
fn close(&self) {
|
||||
fn close(&mut self) {
|
||||
let (chan, port) = channel();
|
||||
self.chan.send(Exit(chan));
|
||||
self.send(Exit(chan));
|
||||
port.recv();
|
||||
}
|
||||
|
||||
fn dup(&self) -> Box<ScriptListener+'static> {
|
||||
box self.clone() as Box<ScriptListener+'static>
|
||||
fn dup(&mut self) -> Box<ScriptListener+'static> {
|
||||
box self.clone_compositor_proxy() as Box<ScriptListener+'static>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,21 +110,21 @@ impl LayerProperties {
|
|||
}
|
||||
|
||||
/// Implementation of the abstract `RenderListener` interface.
|
||||
impl RenderListener for CompositorChan {
|
||||
fn get_graphics_metadata(&self) -> Option<NativeGraphicsMetadata> {
|
||||
impl RenderListener for Box<CompositorProxy+'static+Send> {
|
||||
fn get_graphics_metadata(&mut self) -> Option<NativeGraphicsMetadata> {
|
||||
let (chan, port) = channel();
|
||||
self.chan.send(GetGraphicsMetadata(chan));
|
||||
self.send(GetGraphicsMetadata(chan));
|
||||
port.recv()
|
||||
}
|
||||
|
||||
fn paint(&self,
|
||||
fn paint(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
epoch: Epoch,
|
||||
replies: Vec<(LayerId, Box<LayerBufferSet>)>) {
|
||||
self.chan.send(Paint(pipeline_id, epoch, replies));
|
||||
self.send(Paint(pipeline_id, epoch, replies));
|
||||
}
|
||||
|
||||
fn initialize_layers_for_pipeline(&self,
|
||||
fn initialize_layers_for_pipeline(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
metadata: Vec<LayerMetadata>,
|
||||
epoch: Epoch) {
|
||||
|
@ -108,36 +135,23 @@ impl RenderListener for CompositorChan {
|
|||
for metadata in metadata.iter() {
|
||||
let layer_properties = LayerProperties::new(pipeline_id, epoch, metadata);
|
||||
if first {
|
||||
self.chan.send(CreateOrUpdateRootLayer(layer_properties));
|
||||
self.send(CreateOrUpdateRootLayer(layer_properties));
|
||||
first = false
|
||||
} else {
|
||||
self.chan.send(CreateOrUpdateDescendantLayer(layer_properties));
|
||||
self.send(CreateOrUpdateDescendantLayer(layer_properties));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_msg_discarded(&self) {
|
||||
self.chan.send(RenderMsgDiscarded);
|
||||
fn render_msg_discarded(&mut self) {
|
||||
self.send(RenderMsgDiscarded);
|
||||
}
|
||||
|
||||
fn set_render_state(&self, pipeline_id: PipelineId, render_state: RenderState) {
|
||||
self.chan.send(ChangeRenderState(pipeline_id, render_state))
|
||||
fn set_render_state(&mut self, pipeline_id: PipelineId, render_state: RenderState) {
|
||||
self.send(ChangeRenderState(pipeline_id, render_state))
|
||||
}
|
||||
}
|
||||
|
||||
impl CompositorChan {
|
||||
pub fn new() -> (Receiver<Msg>, CompositorChan) {
|
||||
let (chan, port) = channel();
|
||||
let compositor_chan = CompositorChan {
|
||||
chan: chan,
|
||||
};
|
||||
(port, compositor_chan)
|
||||
}
|
||||
|
||||
pub fn send(&self, msg: Msg) {
|
||||
self.chan.send(msg);
|
||||
}
|
||||
}
|
||||
/// Messages from the painting task and the constellation task to the compositor task.
|
||||
pub enum Msg {
|
||||
/// Requests that the compositor shut down.
|
||||
|
@ -177,6 +191,30 @@ pub enum Msg {
|
|||
SetIds(SendableFrameTree, Sender<()>, ConstellationChan),
|
||||
/// The load of a page for a given URL has completed.
|
||||
LoadComplete(PipelineId, Url),
|
||||
/// Indicates that the scrolling timeout with the given starting timestamp has happened and a
|
||||
/// composite should happen. (See the `scrolling` module.)
|
||||
ScrollTimeout(u64),
|
||||
}
|
||||
|
||||
impl Show for Msg {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(),FormatError> {
|
||||
match *self {
|
||||
Exit(..) => write!(f, "Exit"),
|
||||
ShutdownComplete(..) => write!(f, "ShutdownComplete"),
|
||||
GetGraphicsMetadata(..) => write!(f, "GetGraphicsMetadata"),
|
||||
CreateOrUpdateRootLayer(..) => write!(f, "CreateOrUpdateRootLayer"),
|
||||
CreateOrUpdateDescendantLayer(..) => write!(f, "CreateOrUpdateDescendantLayer"),
|
||||
SetLayerOrigin(..) => write!(f, "SetLayerOrigin"),
|
||||
ScrollFragmentPoint(..) => write!(f, "ScrollFragmentPoint"),
|
||||
Paint(..) => write!(f, "Paint"),
|
||||
ChangeReadyState(..) => write!(f, "ChangeReadyState"),
|
||||
ChangeRenderState(..) => write!(f, "ChangeRenderState"),
|
||||
RenderMsgDiscarded(..) => write!(f, "RenderMsgDiscarded"),
|
||||
SetIds(..) => write!(f, "SetIds"),
|
||||
LoadComplete(..) => write!(f, "LoadComplete"),
|
||||
ScrollTimeout(..) => write!(f, "ScrollTimeout"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompositorTask;
|
||||
|
@ -196,27 +234,38 @@ impl CompositorTask {
|
|||
NativeCompositingGraphicsContext::new()
|
||||
}
|
||||
|
||||
pub fn create<Window: WindowMethods>(
|
||||
window: Option<Rc<Window>>,
|
||||
port: Receiver<Msg>,
|
||||
pub fn create<Window>(window: Option<Rc<Window>>,
|
||||
sender: Box<CompositorProxy+Send>,
|
||||
receiver: Box<CompositorReceiver>,
|
||||
constellation_chan: ConstellationChan,
|
||||
time_profiler_chan: TimeProfilerChan,
|
||||
memory_profiler_chan: MemoryProfilerChan) {
|
||||
|
||||
memory_profiler_chan: MemoryProfilerChan)
|
||||
-> Box<CompositorEventListener + 'static>
|
||||
where Window: WindowMethods + 'static {
|
||||
match window {
|
||||
Some(window) => {
|
||||
compositor::IOCompositor::create(window,
|
||||
port,
|
||||
box compositor::IOCompositor::create(window,
|
||||
sender,
|
||||
receiver,
|
||||
constellation_chan.clone(),
|
||||
time_profiler_chan,
|
||||
memory_profiler_chan)
|
||||
as Box<CompositorEventListener>
|
||||
}
|
||||
None => {
|
||||
headless::NullCompositor::create(port,
|
||||
box headless::NullCompositor::create(receiver,
|
||||
constellation_chan.clone(),
|
||||
time_profiler_chan,
|
||||
memory_profiler_chan)
|
||||
as Box<CompositorEventListener>
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CompositorEventListener {
|
||||
fn handle_event(&mut self, event: WindowEvent) -> bool;
|
||||
fn repaint_synchronously(&mut self);
|
||||
fn shutdown(&mut self);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,53 +2,79 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use compositor_task::{CompositorChan, LoadComplete, ShutdownComplete, SetLayerOrigin, SetIds};
|
||||
use pipeline::{Pipeline, CompositionPipeline};
|
||||
|
||||
use compositor_task::{CompositorProxy, LoadComplete, ShutdownComplete, SetLayerOrigin, SetIds};
|
||||
use devtools_traits::DevtoolsControlChan;
|
||||
use std::collections::hashmap::{HashMap, HashSet};
|
||||
use geom::rect::{Rect, TypedRect};
|
||||
use geom::scale_factor::ScaleFactor;
|
||||
use gfx::font_cache_task::FontCacheTask;
|
||||
use gfx::render_task;
|
||||
use libc;
|
||||
use pipeline::{Pipeline, CompositionPipeline};
|
||||
use layers::geometry::DevicePixel;
|
||||
use layout_traits::{LayoutControlChan, LayoutTaskFactory, ExitNowMsg};
|
||||
use libc;
|
||||
use script_traits::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg};
|
||||
use script_traits::{ScriptControlChan, ScriptTaskFactory};
|
||||
use servo_msg::compositor_msg::LayerId;
|
||||
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg};
|
||||
use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
|
||||
use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg, NavigateMsg};
|
||||
use servo_msg::constellation_msg::{LoadData, NavigationType, PipelineId, RendererReadyMsg, ResizedWindowMsg};
|
||||
use servo_msg::constellation_msg::{SubpageId, WindowSizeData};
|
||||
use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg};
|
||||
use servo_msg::constellation_msg::{LoadData, NavigateMsg, NavigationType, PipelineId};
|
||||
use servo_msg::constellation_msg::{RendererReadyMsg, ResizedWindowMsg, SubpageId, WindowSizeData};
|
||||
use servo_msg::constellation_msg;
|
||||
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
|
||||
use gfx::font_cache_task::FontCacheTask;
|
||||
use servo_net::resource_task::ResourceTask;
|
||||
use servo_net::resource_task;
|
||||
use servo_util::geometry::PagePx;
|
||||
use servo_util::geometry::{PagePx, ViewportPx};
|
||||
use servo_util::opts;
|
||||
use servo_util::time::TimeProfilerChan;
|
||||
use servo_util::task::spawn_named;
|
||||
use servo_util::time::TimeProfilerChan;
|
||||
use std::cell::RefCell;
|
||||
use std::mem::replace;
|
||||
use std::collections::hashmap::{HashMap, HashSet};
|
||||
use std::io;
|
||||
use std::mem::replace;
|
||||
use std::rc::Rc;
|
||||
use url::Url;
|
||||
|
||||
/// Maintains the pipelines and navigation context and grants permission to composite
|
||||
/// Maintains the pipelines and navigation context and grants permission to composite.
|
||||
pub struct Constellation<LTF, STF> {
|
||||
/// A channel through which messages can be sent to this object.
|
||||
pub chan: ConstellationChan,
|
||||
|
||||
/// Receives messages.
|
||||
pub request_port: Receiver<Msg>,
|
||||
pub compositor_chan: CompositorChan,
|
||||
|
||||
/// A channel (the implementation of which is port-specific) through which messages can be sent
|
||||
/// to the compositor.
|
||||
pub compositor_proxy: Box<CompositorProxy>,
|
||||
|
||||
/// A channel through which messages can be sent to the resource task.
|
||||
pub resource_task: ResourceTask,
|
||||
|
||||
/// A channel through which messages can be sent to the image cache task.
|
||||
pub image_cache_task: ImageCacheTask,
|
||||
|
||||
/// A channel through which messages can be sent to the developer tools.
|
||||
devtools_chan: Option<DevtoolsControlChan>,
|
||||
|
||||
/// A list of all the pipelines. (See the `pipeline` module for more details.)
|
||||
pipelines: HashMap<PipelineId, Rc<Pipeline>>,
|
||||
|
||||
/// A channel through which messages can be sent to the font cache.
|
||||
font_cache_task: FontCacheTask,
|
||||
|
||||
navigation_context: NavigationContext,
|
||||
|
||||
/// The next free ID to assign to a pipeline.
|
||||
next_pipeline_id: PipelineId,
|
||||
|
||||
pending_frames: Vec<FrameChange>,
|
||||
|
||||
pending_sizes: HashMap<(PipelineId, SubpageId), TypedRect<PagePx, f32>>,
|
||||
|
||||
/// A channel through which messages can be sent to the time profiler.
|
||||
pub time_profiler_chan: TimeProfilerChan,
|
||||
|
||||
pub window_size: WindowSizeData,
|
||||
}
|
||||
|
||||
|
@ -86,7 +112,11 @@ impl FrameTree {
|
|||
fn to_sendable(&self) -> SendableFrameTree {
|
||||
let sendable_frame_tree = SendableFrameTree {
|
||||
pipeline: self.pipeline.to_sendable(),
|
||||
children: self.children.borrow().iter().map(|frame_tree| frame_tree.to_sendable()).collect(),
|
||||
children: self.children
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|frame_tree| frame_tree.to_sendable())
|
||||
.collect(),
|
||||
};
|
||||
sendable_frame_tree
|
||||
}
|
||||
|
@ -109,8 +139,8 @@ impl FrameTreeTraversal for Rc<FrameTree> {
|
|||
self.iter().find(|frame_tree| id == frame_tree.pipeline.id)
|
||||
}
|
||||
|
||||
/// Replaces a node of the frame tree in place. Returns the node that was removed or the original node
|
||||
/// if the node to replace could not be found.
|
||||
/// Replaces a node of the frame tree in place. Returns the node that was removed or the
|
||||
/// original node if the node to replace could not be found.
|
||||
fn replace_child(&self, id: PipelineId, new_child: Rc<FrameTree>) -> ReplaceResult {
|
||||
for frame_tree in self.iter() {
|
||||
let mut children = frame_tree.children.borrow_mut();
|
||||
|
@ -239,7 +269,7 @@ impl NavigationContext {
|
|||
}
|
||||
|
||||
impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
||||
pub fn start(compositor_chan: CompositorChan,
|
||||
pub fn start(compositor_proxy: Box<CompositorProxy+Send>,
|
||||
resource_task: ResourceTask,
|
||||
image_cache_task: ImageCacheTask,
|
||||
font_cache_task: FontCacheTask,
|
||||
|
@ -249,10 +279,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
let (constellation_port, constellation_chan) = ConstellationChan::new();
|
||||
let constellation_chan_clone = constellation_chan.clone();
|
||||
spawn_named("Constellation", proc() {
|
||||
let mut constellation : Constellation<LTF, STF> = Constellation {
|
||||
let mut constellation: Constellation<LTF, STF> = Constellation {
|
||||
chan: constellation_chan_clone,
|
||||
request_port: constellation_port,
|
||||
compositor_chan: compositor_chan,
|
||||
compositor_proxy: compositor_proxy,
|
||||
devtools_chan: devtools_chan,
|
||||
resource_task: resource_task,
|
||||
image_cache_task: image_cache_task,
|
||||
|
@ -293,7 +323,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
let pipe = Pipeline::create::<LTF, STF>(id,
|
||||
subpage_id,
|
||||
self.chan.clone(),
|
||||
self.compositor_chan.clone(),
|
||||
self.compositor_proxy.clone_compositor_proxy(),
|
||||
self.devtools_chan.clone(),
|
||||
self.image_cache_task.clone(),
|
||||
self.font_cache_task.clone(),
|
||||
|
@ -367,7 +397,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
// script, and reflow messages have been sent.
|
||||
LoadCompleteMsg(pipeline_id, url) => {
|
||||
debug!("constellation got load complete message");
|
||||
self.compositor_chan.send(LoadComplete(pipeline_id, url));
|
||||
self.compositor_proxy.send(LoadComplete(pipeline_id, url));
|
||||
}
|
||||
// Handle a forward or back request
|
||||
NavigateMsg(direction) => {
|
||||
|
@ -387,14 +417,14 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
true
|
||||
}
|
||||
|
||||
fn handle_exit(&self) {
|
||||
fn handle_exit(&mut self) {
|
||||
for (_id, ref pipeline) in self.pipelines.iter() {
|
||||
pipeline.exit();
|
||||
}
|
||||
self.image_cache_task.exit();
|
||||
self.resource_task.send(resource_task::Exit);
|
||||
self.font_cache_task.exit();
|
||||
self.compositor_chan.send(ShutdownComplete);
|
||||
self.compositor_proxy.send(ShutdownComplete);
|
||||
}
|
||||
|
||||
fn handle_failure_msg(&mut self, pipeline_id: PipelineId, subpage_id: Option<SubpageId>) {
|
||||
|
@ -491,10 +521,56 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
let frames = self.find_all(pipeline_id);
|
||||
|
||||
{
|
||||
// If the subframe is in the current frame tree, the compositor needs the new size
|
||||
for current_frame in self.navigation_context.current.iter() {
|
||||
debug!("Constellation: Sending size for frame in current frame tree.");
|
||||
let source_frame = current_frame.find(pipeline_id);
|
||||
for source_frame in source_frame.iter() {
|
||||
let mut children = source_frame.children.borrow_mut();
|
||||
match children.iter_mut().find(|child| subpage_eq(child)) {
|
||||
None => {}
|
||||
Some(child) => {
|
||||
update_child_rect(child,
|
||||
rect,
|
||||
true,
|
||||
&mut already_sent,
|
||||
&mut self.compositor_proxy,
|
||||
self.window_size.device_pixel_ratio)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update all frames with matching pipeline- and subpage-ids
|
||||
for frame_tree in frames.iter() {
|
||||
let mut children = frame_tree.children.borrow_mut();
|
||||
let found_child = children.iter_mut().find(|child| subpage_eq(child));
|
||||
found_child.map(|child| {
|
||||
update_child_rect(child,
|
||||
rect,
|
||||
false,
|
||||
&mut already_sent,
|
||||
&mut self.compositor_proxy,
|
||||
self.window_size.device_pixel_ratio)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, if no pipelines were sent a resize msg, then this subpage id
|
||||
// should be added to pending sizes
|
||||
if already_sent.len() == 0 {
|
||||
self.pending_sizes.insert((pipeline_id, subpage_id), rect);
|
||||
}
|
||||
|
||||
// Update a child's frame rect and inform its script task of the change,
|
||||
// if it hasn't been already. Optionally inform the compositor if
|
||||
// resize happens immediately.
|
||||
let update_child_rect = |child_frame_tree: &mut ChildFrameTree, is_active: bool| {
|
||||
fn update_child_rect(child_frame_tree: &mut ChildFrameTree,
|
||||
rect: TypedRect<PagePx,f32>,
|
||||
is_active: bool,
|
||||
already_sent: &mut HashSet<PipelineId>,
|
||||
compositor_proxy: &mut Box<CompositorProxy>,
|
||||
device_pixel_ratio: ScaleFactor<ViewportPx,DevicePixel,f32>) {
|
||||
child_frame_tree.rect = Some(rect);
|
||||
// NOTE: work around borrowchk issues
|
||||
let pipeline = &child_frame_tree.frame_tree.pipeline;
|
||||
|
@ -504,42 +580,18 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
script_chan.send(ResizeMsg(pipeline.id, WindowSizeData {
|
||||
visible_viewport: rect.size,
|
||||
initial_viewport: rect.size * ScaleFactor(1.0),
|
||||
device_pixel_ratio: self.window_size.device_pixel_ratio,
|
||||
device_pixel_ratio: device_pixel_ratio,
|
||||
}));
|
||||
self.compositor_chan.send(SetLayerOrigin(pipeline.id,
|
||||
compositor_proxy.send(SetLayerOrigin(pipeline.id,
|
||||
LayerId::null(),
|
||||
rect.to_untyped().origin));
|
||||
} else {
|
||||
already_sent.insert(pipeline.id);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// If the subframe is in the current frame tree, the compositor needs the new size
|
||||
for current_frame in self.current_frame().iter() {
|
||||
debug!("Constellation: Sending size for frame in current frame tree.");
|
||||
let source_frame = current_frame.find(pipeline_id);
|
||||
for source_frame in source_frame.iter() {
|
||||
let mut children = source_frame.children.borrow_mut();
|
||||
let found_child = children.iter_mut().find(|child| subpage_eq(child));
|
||||
found_child.map(|child| update_child_rect(child, true));
|
||||
}
|
||||
}
|
||||
|
||||
// Update all frames with matching pipeline- and subpage-ids
|
||||
for frame_tree in frames.iter() {
|
||||
let mut children = frame_tree.children.borrow_mut();
|
||||
let found_child = children.iter_mut().find(|child| subpage_eq(child));
|
||||
found_child.map(|child| update_child_rect(child, false));
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, if no pipelines were sent a resize msg, then this subpage id
|
||||
// should be added to pending sizes
|
||||
if already_sent.len() == 0 {
|
||||
self.pending_sizes.insert((pipeline_id, subpage_id), rect);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_load_iframe_url_msg(&mut self,
|
||||
url: Url,
|
||||
|
@ -851,10 +903,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_ids(&self, frame_tree: &Rc<FrameTree>) {
|
||||
fn set_ids(&mut self, frame_tree: &Rc<FrameTree>) {
|
||||
let (chan, port) = channel();
|
||||
debug!("Constellation sending SetIds");
|
||||
self.compositor_chan.send(SetIds(frame_tree.to_sendable(), chan, self.chan.clone()));
|
||||
self.compositor_proxy.send(SetIds(frame_tree.to_sendable(), chan, self.chan.clone()));
|
||||
match port.recv_opt() {
|
||||
Ok(()) => {
|
||||
let mut iter = frame_tree.iter();
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use compositor_task::{Msg, Exit, ChangeReadyState, SetIds};
|
||||
use compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpdateDescendantLayer};
|
||||
use compositor_task::{SetLayerOrigin, Paint, ScrollFragmentPoint, LoadComplete};
|
||||
use compositor_task::{ShutdownComplete, ChangeRenderState, RenderMsgDiscarded};
|
||||
use compositor_task::{Exit, ChangeReadyState, LoadComplete, Paint, ScrollFragmentPoint, SetIds};
|
||||
use compositor_task::{SetLayerOrigin, ShutdownComplete, ChangeRenderState, RenderMsgDiscarded};
|
||||
use compositor_task::{CompositorEventListener, CompositorReceiver, ScrollTimeout};
|
||||
use windowing::WindowEvent;
|
||||
|
||||
use geom::scale_factor::ScaleFactor;
|
||||
use geom::size::TypedSize2D;
|
||||
|
@ -21,59 +22,66 @@ use servo_util::time;
|
|||
/// It's intended for headless testing.
|
||||
pub struct NullCompositor {
|
||||
/// The port on which we receive messages.
|
||||
pub port: Receiver<Msg>,
|
||||
pub port: Box<CompositorReceiver>,
|
||||
/// A channel to the constellation.
|
||||
constellation_chan: ConstellationChan,
|
||||
/// A channel to the time profiler.
|
||||
time_profiler_chan: TimeProfilerChan,
|
||||
/// A channel to the memory profiler.
|
||||
memory_profiler_chan: MemoryProfilerChan,
|
||||
}
|
||||
|
||||
impl NullCompositor {
|
||||
fn new(port: Receiver<Msg>) -> NullCompositor {
|
||||
fn new(port: Box<CompositorReceiver>,
|
||||
constellation_chan: ConstellationChan,
|
||||
time_profiler_chan: TimeProfilerChan,
|
||||
memory_profiler_chan: MemoryProfilerChan)
|
||||
-> NullCompositor {
|
||||
NullCompositor {
|
||||
port: port,
|
||||
constellation_chan: constellation_chan,
|
||||
time_profiler_chan: time_profiler_chan,
|
||||
memory_profiler_chan: memory_profiler_chan,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(port: Receiver<Msg>,
|
||||
pub fn create(port: Box<CompositorReceiver>,
|
||||
constellation_chan: ConstellationChan,
|
||||
time_profiler_chan: TimeProfilerChan,
|
||||
memory_profiler_chan: MemoryProfilerChan) {
|
||||
let compositor = NullCompositor::new(port);
|
||||
memory_profiler_chan: MemoryProfilerChan)
|
||||
-> NullCompositor {
|
||||
let compositor = NullCompositor::new(port,
|
||||
constellation_chan,
|
||||
time_profiler_chan,
|
||||
memory_profiler_chan);
|
||||
|
||||
// Tell the constellation about the initial fake size.
|
||||
{
|
||||
let ConstellationChan(ref chan) = constellation_chan;
|
||||
let ConstellationChan(ref chan) = compositor.constellation_chan;
|
||||
chan.send(ResizedWindowMsg(WindowSizeData {
|
||||
initial_viewport: TypedSize2D(640_f32, 480_f32),
|
||||
visible_viewport: TypedSize2D(640_f32, 480_f32),
|
||||
device_pixel_ratio: ScaleFactor(1.0),
|
||||
}));
|
||||
}
|
||||
compositor.handle_message(constellation_chan);
|
||||
|
||||
// Drain compositor port, sometimes messages contain channels that are blocking
|
||||
// another task from finishing (i.e. SetIds)
|
||||
loop {
|
||||
match compositor.port.try_recv() {
|
||||
Err(_) => break,
|
||||
Ok(_) => {},
|
||||
}
|
||||
compositor
|
||||
}
|
||||
}
|
||||
|
||||
time_profiler_chan.send(time::ExitMsg);
|
||||
memory_profiler_chan.send(memory::ExitMsg);
|
||||
}
|
||||
|
||||
fn handle_message(&self, constellation_chan: ConstellationChan) {
|
||||
loop {
|
||||
match self.port.recv() {
|
||||
impl CompositorEventListener for NullCompositor {
|
||||
fn handle_event(&mut self, _: WindowEvent) -> bool {
|
||||
match self.port.recv_compositor_msg() {
|
||||
Exit(chan) => {
|
||||
debug!("shutting down the constellation");
|
||||
let ConstellationChan(ref con_chan) = constellation_chan;
|
||||
let ConstellationChan(ref con_chan) = self.constellation_chan;
|
||||
con_chan.send(ExitMsg);
|
||||
chan.send(());
|
||||
}
|
||||
|
||||
ShutdownComplete => {
|
||||
debug!("constellation completed shutdown");
|
||||
break
|
||||
return false
|
||||
}
|
||||
|
||||
GetGraphicsMetadata(chan) => {
|
||||
|
@ -92,8 +100,19 @@ impl NullCompositor {
|
|||
CreateOrUpdateDescendantLayer(..) |
|
||||
SetLayerOrigin(..) | Paint(..) |
|
||||
ChangeReadyState(..) | ChangeRenderState(..) | ScrollFragmentPoint(..) |
|
||||
LoadComplete(..) | RenderMsgDiscarded(..) => ()
|
||||
LoadComplete(..) | RenderMsgDiscarded(..) | ScrollTimeout(..) => ()
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn repaint_synchronously(&mut self) {}
|
||||
|
||||
fn shutdown(&mut self) {
|
||||
// Drain compositor port, sometimes messages contain channels that are blocking
|
||||
// another task from finishing (i.e. SetIds)
|
||||
while self.port.try_recv_compositor_msg().is_some() {}
|
||||
|
||||
self.time_profiler_chan.send(time::ExitMsg);
|
||||
self.memory_profiler_chan.send(memory::ExitMsg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ extern crate "util" as servo_util;
|
|||
extern crate gleam;
|
||||
|
||||
extern crate libc;
|
||||
extern crate native;
|
||||
extern crate time;
|
||||
extern crate url;
|
||||
|
||||
|
@ -38,12 +39,14 @@ extern crate core_graphics;
|
|||
#[cfg(target_os="macos")]
|
||||
extern crate core_text;
|
||||
|
||||
pub use compositor_task::{CompositorChan, CompositorTask};
|
||||
pub use compositor_task::{CompositorEventListener, CompositorProxy, CompositorTask};
|
||||
pub use constellation::Constellation;
|
||||
|
||||
pub mod compositor_task;
|
||||
|
||||
mod compositor_layer;
|
||||
mod scrolling;
|
||||
|
||||
mod compositor;
|
||||
mod headless;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use CompositorChan;
|
||||
use CompositorProxy;
|
||||
use layout_traits::{LayoutTaskFactory, LayoutControlChan};
|
||||
use script_traits::{ScriptControlChan, ScriptTaskFactory};
|
||||
use script_traits::{AttachLayoutMsg, LoadMsg, NewLayoutInfo, ExitPipelineMsg};
|
||||
|
@ -47,7 +47,7 @@ impl Pipeline {
|
|||
id: PipelineId,
|
||||
subpage_id: Option<SubpageId>,
|
||||
constellation_chan: ConstellationChan,
|
||||
compositor_chan: CompositorChan,
|
||||
compositor_proxy: Box<CompositorProxy+'static+Send>,
|
||||
devtools_chan: Option<DevtoolsControlChan>,
|
||||
image_cache_task: ImageCacheTask,
|
||||
font_cache_task: FontCacheTask,
|
||||
|
@ -73,7 +73,7 @@ impl Pipeline {
|
|||
let (script_chan, script_port) = channel();
|
||||
ScriptTaskFactory::create(None::<&mut STF>,
|
||||
id,
|
||||
box compositor_chan.clone(),
|
||||
compositor_proxy.clone_compositor_proxy(),
|
||||
&layout_pair,
|
||||
ScriptControlChan(script_chan.clone()),
|
||||
script_port,
|
||||
|
@ -101,7 +101,7 @@ impl Pipeline {
|
|||
|
||||
RenderTask::create(id,
|
||||
render_port,
|
||||
compositor_chan.clone(),
|
||||
compositor_proxy,
|
||||
constellation_chan.clone(),
|
||||
font_cache_task.clone(),
|
||||
failure.clone(),
|
||||
|
|
73
components/compositing/scrolling.rs
Normal file
73
components/compositing/scrolling.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! A timer thread that gives the painting task a little time to catch up when the user scrolls.
|
||||
|
||||
use compositor_task::{CompositorProxy, ScrollTimeout};
|
||||
|
||||
use native::task::NativeTaskBuilder;
|
||||
use std::io::timer;
|
||||
use std::task::TaskBuilder;
|
||||
use std::time::duration::Duration;
|
||||
use time;
|
||||
|
||||
/// The amount of time in nanoseconds that we give to the painting thread to paint new tiles upon
|
||||
/// processing a scroll event that caused new tiles to be revealed. When this expires, we give up
|
||||
/// and composite anyway (showing a "checkerboard") to avoid dropping the frame.
|
||||
static TIMEOUT: i64 = 12_000_000;
|
||||
|
||||
pub struct ScrollingTimerProxy {
|
||||
sender: Sender<ToScrollingTimerMsg>,
|
||||
}
|
||||
|
||||
pub struct ScrollingTimer {
|
||||
compositor_proxy: Box<CompositorProxy>,
|
||||
receiver: Receiver<ToScrollingTimerMsg>,
|
||||
}
|
||||
|
||||
enum ToScrollingTimerMsg {
|
||||
ExitMsg,
|
||||
ScrollEventProcessedMsg(u64),
|
||||
}
|
||||
|
||||
impl ScrollingTimerProxy {
|
||||
pub fn new(compositor_proxy: Box<CompositorProxy+Send>) -> ScrollingTimerProxy {
|
||||
let (to_scrolling_timer_sender, to_scrolling_timer_receiver) = channel();
|
||||
TaskBuilder::new().native().spawn(proc() {
|
||||
let mut scrolling_timer = ScrollingTimer {
|
||||
compositor_proxy: compositor_proxy,
|
||||
receiver: to_scrolling_timer_receiver,
|
||||
};
|
||||
scrolling_timer.run();
|
||||
});
|
||||
ScrollingTimerProxy {
|
||||
sender: to_scrolling_timer_sender,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scroll_event_processed(&mut self, timestamp: u64) {
|
||||
self.sender.send(ScrollEventProcessedMsg(timestamp))
|
||||
}
|
||||
|
||||
pub fn shutdown(&mut self) {
|
||||
self.sender.send(ExitMsg);
|
||||
}
|
||||
}
|
||||
|
||||
impl ScrollingTimer {
|
||||
pub fn run(&mut self) {
|
||||
loop {
|
||||
match self.receiver.recv_opt() {
|
||||
Ok(ScrollEventProcessedMsg(timestamp)) => {
|
||||
let target = timestamp as i64 + TIMEOUT;
|
||||
let delta = target - (time::precise_time_ns() as i64);
|
||||
timer::sleep(Duration::nanoseconds(delta));
|
||||
self.compositor_proxy.send(ScrollTimeout(timestamp));
|
||||
}
|
||||
Ok(ExitMsg) | Err(_) => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
//! Abstract windowing methods. The concrete implementations of these can be found in `platform/`.
|
||||
|
||||
use compositor_task::{CompositorProxy, CompositorReceiver};
|
||||
|
||||
use geom::point::TypedPoint2D;
|
||||
use geom::scale_factor::ScaleFactor;
|
||||
use geom::size::TypedSize2D;
|
||||
|
@ -11,6 +13,8 @@ use layers::geometry::DevicePixel;
|
|||
use layers::platform::surface::NativeGraphicsMetadata;
|
||||
use servo_msg::compositor_msg::{ReadyState, RenderState};
|
||||
use servo_util::geometry::ScreenPx;
|
||||
use std::fmt::{FormatError, Formatter, Show};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub enum MouseWindowEvent {
|
||||
MouseWindowClickEvent(uint, TypedPoint2D<DevicePixel, f32>),
|
||||
|
@ -25,10 +29,12 @@ pub enum WindowNavigateMsg {
|
|||
|
||||
/// Events that the windowing system sends to Servo.
|
||||
pub enum WindowEvent {
|
||||
/// Sent when no message has arrived.
|
||||
/// Sent when no message has arrived, but the event loop was kicked for some reason (perhaps
|
||||
/// by another Servo subsystem).
|
||||
///
|
||||
/// FIXME: This is a bogus event and is only used because we don't have the new
|
||||
/// scheduler integrated with the platform event loop.
|
||||
/// FIXME(pcwalton): This is kind of ugly and may not work well with multiprocess Servo.
|
||||
/// It's possible that this should be something like
|
||||
/// `CompositorMessageWindowEvent(compositor_task::Msg)` instead.
|
||||
IdleWindowEvent,
|
||||
/// Sent when part of the window is marked dirty and needs to be redrawn.
|
||||
RefreshWindowEvent,
|
||||
|
@ -54,6 +60,25 @@ pub enum WindowEvent {
|
|||
QuitWindowEvent,
|
||||
}
|
||||
|
||||
impl Show for WindowEvent {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result<(),FormatError> {
|
||||
match *self {
|
||||
IdleWindowEvent => write!(f, "Idle"),
|
||||
RefreshWindowEvent => write!(f, "Refresh"),
|
||||
ResizeWindowEvent(..) => write!(f, "Resize"),
|
||||
LoadUrlWindowEvent(..) => write!(f, "LoadUrl"),
|
||||
MouseWindowEventClass(..) => write!(f, "Mouse"),
|
||||
MouseWindowMoveEventClass(..) => write!(f, "MouseMove"),
|
||||
ScrollWindowEvent(..) => write!(f, "Scroll"),
|
||||
ZoomWindowEvent(..) => write!(f, "Zoom"),
|
||||
PinchZoomWindowEvent(..) => write!(f, "PinchZoom"),
|
||||
NavigationWindowEvent(..) => write!(f, "Navigation"),
|
||||
FinishedWindowEvent => write!(f, "Finished"),
|
||||
QuitWindowEvent => write!(f, "Quit"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WindowMethods {
|
||||
/// Returns the size of the window in hardware pixels.
|
||||
fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint>;
|
||||
|
@ -62,9 +87,6 @@ pub trait WindowMethods {
|
|||
/// Presents the window to the screen (perhaps by page flipping).
|
||||
fn present(&self);
|
||||
|
||||
/// Spins the event loop and returns the next event.
|
||||
fn recv(&self) -> WindowEvent;
|
||||
|
||||
/// Sets the ready state of the current page.
|
||||
fn set_ready_state(&self, ready_state: ReadyState);
|
||||
/// Sets the render state of the current page.
|
||||
|
@ -75,5 +97,13 @@ pub trait WindowMethods {
|
|||
|
||||
/// Gets the OS native graphics information for this window.
|
||||
fn native_metadata(&self) -> NativeGraphicsMetadata;
|
||||
|
||||
/// Creates a channel to the compositor. The dummy parameter is needed because we don't have
|
||||
/// UFCS in Rust yet.
|
||||
///
|
||||
/// This is part of the windowing system because its implementation often involves OS-specific
|
||||
/// magic to wake the up window's event loop.
|
||||
fn create_compositor_channel(_: &Option<Rc<Self>>)
|
||||
-> (Box<CompositorProxy+Send>, Box<CompositorReceiver>);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ use render_context::RenderContext;
|
|||
use text::glyph::CharIndex;
|
||||
use text::TextRun;
|
||||
|
||||
use azure::azure::AzFloat;
|
||||
use collections::Deque;
|
||||
use collections::dlist::{mod, DList};
|
||||
use geom::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D};
|
||||
|
@ -26,18 +27,11 @@ use libc::uintptr_t;
|
|||
use servo_net::image::base::Image;
|
||||
use servo_util::dlist as servo_dlist;
|
||||
use servo_util::geometry::Au;
|
||||
use servo_util::opts;
|
||||
use servo_util::range::Range;
|
||||
use std::fmt;
|
||||
use std::slice::Items;
|
||||
use style::computed_values::border_style;
|
||||
use sync::Arc;
|
||||
use std::num::Zero;
|
||||
use std::ptr;
|
||||
|
||||
use azure::AzFloat;
|
||||
use azure::scaled_font::ScaledFont;
|
||||
use azure::azure_hl::ColorPattern;
|
||||
|
||||
pub mod optimizer;
|
||||
|
||||
|
@ -58,88 +52,6 @@ impl OpaqueNode {
|
|||
}
|
||||
}
|
||||
|
||||
trait ScaledFontExtensionMethods {
|
||||
fn draw_text_into_context(&self,
|
||||
rctx: &RenderContext,
|
||||
run: &Box<TextRun>,
|
||||
range: &Range<CharIndex>,
|
||||
baseline_origin: Point2D<Au>,
|
||||
color: Color,
|
||||
antialias: bool);
|
||||
}
|
||||
|
||||
impl ScaledFontExtensionMethods for ScaledFont {
|
||||
fn draw_text_into_context(&self,
|
||||
rctx: &RenderContext,
|
||||
run: &Box<TextRun>,
|
||||
range: &Range<CharIndex>,
|
||||
baseline_origin: Point2D<Au>,
|
||||
color: Color,
|
||||
antialias: bool) {
|
||||
use libc::types::common::c99::uint32_t;
|
||||
use azure::{struct__AzDrawOptions,
|
||||
struct__AzGlyph,
|
||||
struct__AzGlyphBuffer,
|
||||
struct__AzPoint};
|
||||
use azure::azure::{AzDrawTargetFillGlyphs};
|
||||
|
||||
let target = rctx.get_draw_target();
|
||||
let pattern = ColorPattern::new(color);
|
||||
let azure_pattern = pattern.azure_color_pattern;
|
||||
assert!(azure_pattern.is_not_null());
|
||||
|
||||
let fields = if antialias {
|
||||
0x0200
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let mut options = struct__AzDrawOptions {
|
||||
mAlpha: 1f64 as AzFloat,
|
||||
fields: fields,
|
||||
};
|
||||
|
||||
let mut origin = baseline_origin.clone();
|
||||
let mut azglyphs = vec!();
|
||||
azglyphs.reserve(range.length().to_uint());
|
||||
|
||||
for (glyphs, _offset, slice_range) in run.iter_slices_for_range(range) {
|
||||
for (_i, glyph) in glyphs.iter_glyphs_for_char_range(&slice_range) {
|
||||
let glyph_advance = glyph.advance();
|
||||
let glyph_offset = glyph.offset().unwrap_or(Zero::zero());
|
||||
|
||||
let azglyph = struct__AzGlyph {
|
||||
mIndex: glyph.id() as uint32_t,
|
||||
mPosition: struct__AzPoint {
|
||||
x: (origin.x + glyph_offset.x).to_subpx() as AzFloat,
|
||||
y: (origin.y + glyph_offset.y).to_subpx() as AzFloat
|
||||
}
|
||||
};
|
||||
origin = Point2D(origin.x + glyph_advance, origin.y);
|
||||
azglyphs.push(azglyph)
|
||||
};
|
||||
}
|
||||
|
||||
let azglyph_buf_len = azglyphs.len();
|
||||
if azglyph_buf_len == 0 { return; } // Otherwise the Quartz backend will assert.
|
||||
|
||||
let mut glyphbuf = struct__AzGlyphBuffer {
|
||||
mGlyphs: azglyphs.as_mut_ptr(),
|
||||
mNumGlyphs: azglyph_buf_len as uint32_t
|
||||
};
|
||||
|
||||
unsafe {
|
||||
// TODO(Issue #64): this call needs to move into azure_hl.rs
|
||||
AzDrawTargetFillGlyphs(target.azure_draw_target,
|
||||
self.get_ref(),
|
||||
&mut glyphbuf,
|
||||
azure_pattern,
|
||||
&mut options,
|
||||
ptr::null_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// "Steps" as defined by CSS 2.1 § E.2.
|
||||
#[deriving(Clone, PartialEq, Show)]
|
||||
pub enum StackingLevel {
|
||||
|
@ -543,55 +455,7 @@ impl DisplayItem {
|
|||
|
||||
TextDisplayItemClass(ref text) => {
|
||||
debug!("Drawing text at {}.", text.base.bounds);
|
||||
|
||||
// Optimization: Don’t set a transform matrix for upright text,
|
||||
// and pass a strart point to `draw_text_into_context`.
|
||||
// For sideways text, it’s easier to do the rotation such that its center
|
||||
// (the baseline’s start point) is at (0, 0) coordinates.
|
||||
let baseline_origin = match text.orientation {
|
||||
Upright => text.baseline_origin,
|
||||
SidewaysLeft => {
|
||||
let x = text.baseline_origin.x.to_nearest_px() as AzFloat;
|
||||
let y = text.baseline_origin.y.to_nearest_px() as AzFloat;
|
||||
render_context.draw_target.set_transform(¤t_transform.mul(
|
||||
&Matrix2D::new(
|
||||
0., -1.,
|
||||
1., 0.,
|
||||
x, y
|
||||
)
|
||||
));
|
||||
Zero::zero()
|
||||
},
|
||||
SidewaysRight => {
|
||||
let x = text.baseline_origin.x.to_nearest_px() as AzFloat;
|
||||
let y = text.baseline_origin.y.to_nearest_px() as AzFloat;
|
||||
render_context.draw_target.set_transform(¤t_transform.mul(
|
||||
&Matrix2D::new(
|
||||
0., 1.,
|
||||
-1., 0.,
|
||||
x, y
|
||||
)
|
||||
));
|
||||
Zero::zero()
|
||||
}
|
||||
};
|
||||
|
||||
render_context.font_ctx.get_render_font_from_template(
|
||||
&text.text_run.font_template,
|
||||
text.text_run.actual_pt_size
|
||||
).borrow().draw_text_into_context(
|
||||
render_context,
|
||||
&*text.text_run,
|
||||
&text.range,
|
||||
baseline_origin,
|
||||
text.text_color,
|
||||
opts::get().enable_text_antialiasing
|
||||
);
|
||||
|
||||
// Undo the transform, only when we did one.
|
||||
if text.orientation != Upright {
|
||||
render_context.draw_target.set_transform(current_transform)
|
||||
}
|
||||
render_context.draw_text(&**text, current_transform);
|
||||
}
|
||||
|
||||
ImageDisplayItemClass(ref image_item) => {
|
||||
|
|
|
@ -30,6 +30,7 @@ extern crate "util" as servo_util;
|
|||
extern crate "msg" as servo_msg;
|
||||
extern crate style;
|
||||
extern crate sync;
|
||||
extern crate time;
|
||||
extern crate url;
|
||||
|
||||
// Eventually we would like the shaper to be pluggable, as many operating systems have their own
|
||||
|
|
|
@ -2,23 +2,32 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use display_list::{SidewaysLeft, SidewaysRight, TextDisplayItem, Upright};
|
||||
use font_context::FontContext;
|
||||
use style::computed_values::border_style;
|
||||
|
||||
use azure::azure_hl::{B8G8R8A8, A8, Color, ColorPattern, ColorPatternRef, DrawOptions};
|
||||
use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, Linear, SourceOp, StrokeOptions};
|
||||
use azure::AZ_CAP_BUTT;
|
||||
use azure::AzFloat;
|
||||
use azure::azure_hl::{DrawSurfaceOptions,DrawTarget, Linear, SourceOp, StrokeOptions};
|
||||
use azure::scaled_font::ScaledFont;
|
||||
use azure::{AZ_CAP_BUTT, AzDrawTargetFillGlyphs, AzFloat, struct__AzDrawOptions, struct__AzGlyph};
|
||||
use azure::{struct__AzGlyphBuffer, struct__AzPoint};
|
||||
use geom::matrix2d::Matrix2D;
|
||||
use geom::point::Point2D;
|
||||
use geom::rect::Rect;
|
||||
use geom::size::Size2D;
|
||||
use geom::side_offsets::SideOffsets2D;
|
||||
use libc::types::common::c99::uint16_t;
|
||||
use libc::types::common::c99::{uint16_t, uint32_t};
|
||||
use libc::size_t;
|
||||
use png::{RGB8, RGBA8, K8, KA8};
|
||||
use servo_net::image::base::Image;
|
||||
use servo_util::geometry::Au;
|
||||
use servo_util::opts;
|
||||
use servo_util::range::Range;
|
||||
use std::num::Zero;
|
||||
use std::ptr;
|
||||
use sync::Arc;
|
||||
use text::glyph::CharIndex;
|
||||
use text::TextRun;
|
||||
|
||||
pub struct RenderContext<'a> {
|
||||
pub draw_target: &'a DrawTarget,
|
||||
|
@ -390,6 +399,50 @@ impl<'a> RenderContext<'a> {
|
|||
self.draw_border_path(original_bounds, direction, border, scaled_color);
|
||||
}
|
||||
|
||||
pub fn draw_text(&mut self,
|
||||
text: &TextDisplayItem,
|
||||
current_transform: &Matrix2D<AzFloat>) {
|
||||
// Optimization: Don’t set a transform matrix for upright text, and pass a start point to
|
||||
// `draw_text_into_context`.
|
||||
//
|
||||
// For sideways text, it’s easier to do the rotation such that its center (the baseline’s
|
||||
// start point) is at (0, 0) coordinates.
|
||||
let baseline_origin = match text.orientation {
|
||||
Upright => text.baseline_origin,
|
||||
SidewaysLeft => {
|
||||
let x = text.baseline_origin.x.to_subpx() as AzFloat;
|
||||
let y = text.baseline_origin.y.to_subpx() as AzFloat;
|
||||
self.draw_target.set_transform(¤t_transform.mul(&Matrix2D::new(0., -1.,
|
||||
1., 0.,
|
||||
x, y)));
|
||||
Zero::zero()
|
||||
}
|
||||
SidewaysRight => {
|
||||
let x = text.baseline_origin.x.to_subpx() as AzFloat;
|
||||
let y = text.baseline_origin.y.to_subpx() as AzFloat;
|
||||
self.draw_target.set_transform(¤t_transform.mul(&Matrix2D::new(0., 1.,
|
||||
-1., 0.,
|
||||
x, y)));
|
||||
Zero::zero()
|
||||
}
|
||||
};
|
||||
|
||||
self.font_ctx
|
||||
.get_render_font_from_template(&text.text_run.font_template,
|
||||
text.text_run.actual_pt_size)
|
||||
.borrow()
|
||||
.draw_text_into_context(self,
|
||||
&*text.text_run,
|
||||
&text.range,
|
||||
baseline_origin,
|
||||
text.text_color,
|
||||
opts::get().enable_text_antialiasing);
|
||||
|
||||
// Undo the transform, only when we did one.
|
||||
if text.orientation != Upright {
|
||||
self.draw_target.set_transform(current_transform)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait ToAzureRect {
|
||||
|
@ -417,3 +470,78 @@ impl ToSideOffsetsPx for SideOffsets2D<Au> {
|
|||
self.left.to_nearest_px() as AzFloat)
|
||||
}
|
||||
}
|
||||
|
||||
trait ScaledFontExtensionMethods {
|
||||
fn draw_text_into_context(&self,
|
||||
rctx: &RenderContext,
|
||||
run: &Box<TextRun>,
|
||||
range: &Range<CharIndex>,
|
||||
baseline_origin: Point2D<Au>,
|
||||
color: Color,
|
||||
antialias: bool);
|
||||
}
|
||||
|
||||
impl ScaledFontExtensionMethods for ScaledFont {
|
||||
fn draw_text_into_context(&self,
|
||||
rctx: &RenderContext,
|
||||
run: &Box<TextRun>,
|
||||
range: &Range<CharIndex>,
|
||||
baseline_origin: Point2D<Au>,
|
||||
color: Color,
|
||||
antialias: bool) {
|
||||
let target = rctx.get_draw_target();
|
||||
let pattern = ColorPattern::new(color);
|
||||
let azure_pattern = pattern.azure_color_pattern;
|
||||
assert!(azure_pattern.is_not_null());
|
||||
|
||||
let fields = if antialias {
|
||||
0x0200
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let mut options = struct__AzDrawOptions {
|
||||
mAlpha: 1f64 as AzFloat,
|
||||
fields: fields,
|
||||
};
|
||||
|
||||
let mut origin = baseline_origin.clone();
|
||||
let mut azglyphs = vec!();
|
||||
azglyphs.reserve(range.length().to_uint());
|
||||
|
||||
for (glyphs, _offset, slice_range) in run.iter_slices_for_range(range) {
|
||||
for (_i, glyph) in glyphs.iter_glyphs_for_char_range(&slice_range) {
|
||||
let glyph_advance = glyph.advance();
|
||||
let glyph_offset = glyph.offset().unwrap_or(Zero::zero());
|
||||
let azglyph = struct__AzGlyph {
|
||||
mIndex: glyph.id() as uint32_t,
|
||||
mPosition: struct__AzPoint {
|
||||
x: (origin.x + glyph_offset.x).to_subpx() as AzFloat,
|
||||
y: (origin.y + glyph_offset.y).to_subpx() as AzFloat
|
||||
}
|
||||
};
|
||||
origin = Point2D(origin.x + glyph_advance, origin.y);
|
||||
azglyphs.push(azglyph)
|
||||
};
|
||||
}
|
||||
|
||||
let azglyph_buf_len = azglyphs.len();
|
||||
if azglyph_buf_len == 0 { return; } // Otherwise the Quartz backend will assert.
|
||||
|
||||
let mut glyphbuf = struct__AzGlyphBuffer {
|
||||
mGlyphs: azglyphs.as_mut_ptr(),
|
||||
mNumGlyphs: azglyph_buf_len as uint32_t
|
||||
};
|
||||
|
||||
unsafe {
|
||||
// TODO(Issue #64): this call needs to move into azure_hl.rs
|
||||
AzDrawTargetFillGlyphs(target.azure_draw_target,
|
||||
self.get_ref(),
|
||||
&mut glyphbuf,
|
||||
azure_pattern,
|
||||
&mut options,
|
||||
ptr::null_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
use buffer_map::BufferMap;
|
||||
use display_list::optimizer::DisplayListOptimizer;
|
||||
use display_list::DisplayList;
|
||||
use font_cache_task::FontCacheTask;
|
||||
use font_context::FontContext;
|
||||
use render_context::RenderContext;
|
||||
|
||||
|
@ -37,7 +38,6 @@ use std::comm::{Receiver, Sender, channel};
|
|||
use std::mem;
|
||||
use std::task::TaskBuilder;
|
||||
use sync::Arc;
|
||||
use font_cache_task::FontCacheTask;
|
||||
|
||||
/// Information about a layer that layout sends to the painting task.
|
||||
#[deriving(Clone)]
|
||||
|
@ -153,7 +153,10 @@ impl<C> RenderTask<C> where C: RenderListener + Send {
|
|||
shutdown_chan: Sender<()>) {
|
||||
let ConstellationChan(c) = constellation_chan.clone();
|
||||
spawn_named_with_send_on_failure("RenderTask", task_state::Render, proc() {
|
||||
{ // Ensures RenderTask and graphics context are destroyed before shutdown msg
|
||||
{
|
||||
// Ensures that the render task and graphics context are destroyed before the
|
||||
// shutdown message.
|
||||
let mut compositor = compositor;
|
||||
let native_graphics_context = compositor.get_graphics_metadata().map(
|
||||
|md| NativePaintingGraphicsContext::from_metadata(&md));
|
||||
let worker_threads = WorkerThreadProxy::spawn(compositor.get_graphics_metadata(),
|
||||
|
|
|
@ -84,36 +84,36 @@ pub struct LayerMetadata {
|
|||
|
||||
/// The interface used by the renderer to acquire draw targets for each render frame and
|
||||
/// submit them to be drawn to the display.
|
||||
pub trait RenderListener {
|
||||
fn get_graphics_metadata(&self) -> Option<NativeGraphicsMetadata>;
|
||||
pub trait RenderListener for Sized? {
|
||||
fn get_graphics_metadata(&mut self) -> Option<NativeGraphicsMetadata>;
|
||||
|
||||
/// Informs the compositor of the layers for the given pipeline. The compositor responds by
|
||||
/// creating and/or destroying render layers as necessary.
|
||||
fn initialize_layers_for_pipeline(&self,
|
||||
fn initialize_layers_for_pipeline(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
metadata: Vec<LayerMetadata>,
|
||||
epoch: Epoch);
|
||||
|
||||
/// Sends new tiles for the given layer to the compositor.
|
||||
fn paint(&self,
|
||||
fn paint(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
epoch: Epoch,
|
||||
replies: Vec<(LayerId, Box<LayerBufferSet>)>);
|
||||
|
||||
fn render_msg_discarded(&self);
|
||||
fn set_render_state(&self, PipelineId, RenderState);
|
||||
fn render_msg_discarded(&mut self);
|
||||
fn set_render_state(&mut self, PipelineId, RenderState);
|
||||
}
|
||||
|
||||
/// The interface used by the script task to tell the compositor to update its ready state,
|
||||
/// which is used in displaying the appropriate message in the window's title.
|
||||
pub trait ScriptListener : Clone {
|
||||
fn set_ready_state(&self, PipelineId, ReadyState);
|
||||
fn scroll_fragment_point(&self,
|
||||
pub trait ScriptListener {
|
||||
fn set_ready_state(&mut self, PipelineId, ReadyState);
|
||||
fn scroll_fragment_point(&mut self,
|
||||
pipeline_id: PipelineId,
|
||||
layer_id: LayerId,
|
||||
point: Point2D<f32>);
|
||||
fn close(&self);
|
||||
fn dup(&self) -> Box<ScriptListener+'static>;
|
||||
fn close(&mut self);
|
||||
fn dup(&mut self) -> Box<ScriptListener+'static>;
|
||||
}
|
||||
|
||||
impl<E, S: Encoder<E>> Encodable<S, E> for Box<ScriptListener+'static> {
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
* 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/. */
|
||||
|
||||
//! The high-level interface from script to constellation. Using this abstract interface helps reduce
|
||||
//! coupling between these two components
|
||||
//! The high-level interface from script to constellation. Using this abstract interface helps
|
||||
//! reduce coupling between these two components.
|
||||
|
||||
use geom::rect::Rect;
|
||||
use geom::size::TypedSize2D;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use dom::bindings::cell::{DOMRefCell, Ref};
|
||||
use dom::bindings::cell::{DOMRefCell, Ref, RefMut};
|
||||
use dom::bindings::codegen::Bindings::EventHandlerBinding::{OnErrorEventHandlerNonNull, EventHandlerNonNull};
|
||||
use dom::bindings::codegen::Bindings::WindowBinding;
|
||||
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||
|
@ -53,7 +53,7 @@ pub struct Window {
|
|||
location: MutNullableJS<Location>,
|
||||
navigator: MutNullableJS<Navigator>,
|
||||
image_cache_task: ImageCacheTask,
|
||||
compositor: Box<ScriptListener+'static>,
|
||||
compositor: DOMRefCell<Box<ScriptListener+'static>>,
|
||||
browser_context: DOMRefCell<Option<BrowserContext>>,
|
||||
page: Rc<Page>,
|
||||
performance: MutNullableJS<Performance>,
|
||||
|
@ -81,8 +81,8 @@ impl Window {
|
|||
&self.image_cache_task
|
||||
}
|
||||
|
||||
pub fn compositor<'a>(&'a self) -> &'a ScriptListener+'static {
|
||||
&*self.compositor
|
||||
pub fn compositor(&self) -> RefMut<Box<ScriptListener+'static>> {
|
||||
self.compositor.borrow_mut()
|
||||
}
|
||||
|
||||
pub fn browser_context(&self) -> Ref<Option<BrowserContext>> {
|
||||
|
@ -398,7 +398,7 @@ impl Window {
|
|||
script_chan: script_chan,
|
||||
control_chan: control_chan,
|
||||
console: Default::default(),
|
||||
compositor: compositor,
|
||||
compositor: DOMRefCell::new(compositor),
|
||||
page: page,
|
||||
location: Default::default(),
|
||||
navigator: Default::default(),
|
||||
|
|
|
@ -182,7 +182,7 @@ impl Page {
|
|||
if force_reflow {
|
||||
let frame = self.frame();
|
||||
let window = frame.as_ref().unwrap().window.root();
|
||||
self.reflow(reflow_goal, window.control_chan().clone(), window.compositor(), query);
|
||||
self.reflow(reflow_goal, window.control_chan().clone(), &mut **window.compositor(), query);
|
||||
} else {
|
||||
self.avoided_reflows.set(self.avoided_reflows.get() + 1);
|
||||
}
|
||||
|
@ -328,9 +328,8 @@ impl Page {
|
|||
pub fn reflow(&self,
|
||||
goal: ReflowGoal,
|
||||
script_chan: ScriptControlChan,
|
||||
compositor: &ScriptListener,
|
||||
compositor: &mut ScriptListener,
|
||||
query_type: ReflowQueryType) {
|
||||
|
||||
let root = match *self.frame() {
|
||||
None => return,
|
||||
Some(ref frame) => {
|
||||
|
|
|
@ -168,7 +168,7 @@ pub struct ScriptTask {
|
|||
/// For communicating load url messages to the constellation
|
||||
constellation_chan: ConstellationChan,
|
||||
/// A handle to the compositor for communicating ready state messages.
|
||||
compositor: Box<ScriptListener+'static>,
|
||||
compositor: DOMRefCell<Box<ScriptListener+'static>>,
|
||||
|
||||
/// For providing instructions to an optional devtools server.
|
||||
devtools_chan: Option<DevtoolsControlChan>,
|
||||
|
@ -248,10 +248,9 @@ impl ScriptTaskFactory for ScriptTask {
|
|||
box pair.sender() as Box<Any+Send>
|
||||
}
|
||||
|
||||
fn create<C:ScriptListener + Send + 'static>(
|
||||
_phantom: Option<&mut ScriptTask>,
|
||||
fn create<C>(_phantom: Option<&mut ScriptTask>,
|
||||
id: PipelineId,
|
||||
compositor: Box<C>,
|
||||
compositor: C,
|
||||
layout_chan: &OpaqueScriptLayoutChannel,
|
||||
control_chan: ScriptControlChan,
|
||||
control_port: Receiver<ConstellationControlMsg>,
|
||||
|
@ -260,13 +259,14 @@ impl ScriptTaskFactory for ScriptTask {
|
|||
resource_task: ResourceTask,
|
||||
image_cache_task: ImageCacheTask,
|
||||
devtools_chan: Option<DevtoolsControlChan>,
|
||||
window_size: WindowSizeData) {
|
||||
window_size: WindowSizeData)
|
||||
where C: ScriptListener + Send + 'static {
|
||||
let ConstellationChan(const_chan) = constellation_chan.clone();
|
||||
let (script_chan, script_port) = channel();
|
||||
let layout_chan = LayoutChan(layout_chan.sender());
|
||||
spawn_named_with_send_on_failure("ScriptTask", task_state::Script, proc() {
|
||||
let script_task = ScriptTask::new(id,
|
||||
compositor as Box<ScriptListener>,
|
||||
box compositor as Box<ScriptListener>,
|
||||
layout_chan,
|
||||
script_port,
|
||||
ScriptChan(script_chan),
|
||||
|
@ -350,7 +350,7 @@ impl ScriptTask {
|
|||
control_chan: control_chan,
|
||||
control_port: control_port,
|
||||
constellation_chan: constellation_chan,
|
||||
compositor: compositor,
|
||||
compositor: DOMRefCell::new(compositor),
|
||||
devtools_chan: devtools_chan,
|
||||
devtools_port: devtools_receiver,
|
||||
|
||||
|
@ -671,7 +671,7 @@ impl ScriptTask {
|
|||
*layout_join_port = None;
|
||||
}
|
||||
|
||||
self.compositor.set_ready_state(pipeline_id, FinishedLoading);
|
||||
self.compositor.borrow_mut().set_ready_state(pipeline_id, FinishedLoading);
|
||||
|
||||
if page.pending_reflows.get() > 0 {
|
||||
page.pending_reflows.set(0);
|
||||
|
@ -709,7 +709,7 @@ impl ScriptTask {
|
|||
// TODO(tkuehn): currently there is only one window,
|
||||
// so this can afford to be naive and just shut down the
|
||||
// compositor. In the future it'll need to be smarter.
|
||||
self.compositor.close();
|
||||
self.compositor.borrow_mut().close();
|
||||
}
|
||||
|
||||
/// Handles a request to exit the script task and shut down layout.
|
||||
|
@ -771,7 +771,7 @@ impl ScriptTask {
|
|||
page.clone(),
|
||||
self.chan.clone(),
|
||||
self.control_chan.clone(),
|
||||
self.compositor.dup(),
|
||||
self.compositor.borrow_mut().dup(),
|
||||
self.image_cache_task.clone()).root();
|
||||
let doc_url = if is_javascript {
|
||||
let doc_url = last_url.unwrap_or_else(|| {
|
||||
|
@ -787,7 +787,7 @@ impl ScriptTask {
|
|||
|
||||
window.init_browser_context(*document);
|
||||
|
||||
self.compositor.set_ready_state(pipeline_id, Loading);
|
||||
self.compositor.borrow_mut().set_ready_state(pipeline_id, Loading);
|
||||
|
||||
let parser_input = if !is_javascript {
|
||||
InputUrl(url.clone())
|
||||
|
@ -858,7 +858,7 @@ impl ScriptTask {
|
|||
// Really what needs to happen is that this needs to go through layout to ask which
|
||||
// layer the element belongs to, and have it send the scroll message to the
|
||||
// compositor.
|
||||
self.compositor.scroll_fragment_point(pipeline_id, LayerId::null(), point);
|
||||
self.compositor.borrow_mut().scroll_fragment_point(pipeline_id, LayerId::null(), point);
|
||||
}
|
||||
|
||||
fn force_reflow(&self, page: &Page) {
|
||||
|
@ -873,7 +873,10 @@ impl ScriptTask {
|
|||
}
|
||||
|
||||
page.damage();
|
||||
page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor, NoQuery);
|
||||
page.reflow(ReflowForDisplay,
|
||||
self.control_chan.clone(),
|
||||
&mut **self.compositor.borrow_mut(),
|
||||
NoQuery);
|
||||
}
|
||||
|
||||
/// This is the main entry point for receiving and dispatching DOM events.
|
||||
|
|
|
@ -89,9 +89,9 @@ impl<S: Encoder<E>, E> Encodable<S, E> for ScriptControlChan {
|
|||
}
|
||||
|
||||
pub trait ScriptTaskFactory {
|
||||
fn create<C: ScriptListener + Send>(_phantom: Option<&mut Self>,
|
||||
fn create<C>(_phantom: Option<&mut Self>,
|
||||
id: PipelineId,
|
||||
compositor: Box<C>,
|
||||
compositor: C,
|
||||
layout_chan: &OpaqueScriptLayoutChannel,
|
||||
control_chan: ScriptControlChan,
|
||||
control_port: Receiver<ConstellationControlMsg>,
|
||||
|
@ -100,7 +100,9 @@ pub trait ScriptTaskFactory {
|
|||
resource_task: ResourceTask,
|
||||
image_cache_task: ImageCacheTask,
|
||||
devtools_chan: Option<DevtoolsControlChan>,
|
||||
window_size: WindowSizeData);
|
||||
window_size: WindowSizeData)
|
||||
where C: ScriptListener + Send;
|
||||
fn create_layout_channel(_phantom: Option<&mut Self>) -> OpaqueScriptLayoutChannel;
|
||||
fn clone_layout_channel(_phantom: Option<&mut Self>, pair: &OpaqueScriptLayoutChannel) -> Box<Any+Send>;
|
||||
fn clone_layout_channel(_phantom: Option<&mut Self>, pair: &OpaqueScriptLayoutChannel)
|
||||
-> Box<Any+Send>;
|
||||
}
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
|
||||
//! Timing functions.
|
||||
|
||||
use std_time::precise_time_ns;
|
||||
use collections::treemap::TreeMap;
|
||||
use std::comm::{Sender, channel, Receiver};
|
||||
use std::f64;
|
||||
use std::iter::AdditiveIterator;
|
||||
use std::io::timer::sleep;
|
||||
use std::iter::AdditiveIterator;
|
||||
use std::time::duration::Duration;
|
||||
use std_time::precise_time_ns;
|
||||
use task::{spawn_named};
|
||||
use url::Url;
|
||||
|
||||
|
|
10
ports/cef/Cargo.lock
generated
10
ports/cef/Cargo.lock
generated
|
@ -8,7 +8,7 @@ dependencies = [
|
|||
"devtools 0.0.1",
|
||||
"geom 0.1.0 (git+https://github.com/servo/rust-geom#b001a76e907befaae1d0d6dd259418a22092da86)",
|
||||
"gfx 0.0.1",
|
||||
"glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo#a15c2d04b8969aea653841d1d79e5fdf68de664b)",
|
||||
"glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo#cec2861dd75eb721694b3176f94a276d77018bde)",
|
||||
"glfw_app 0.0.1",
|
||||
"js 0.1.0 (git+https://github.com/servo/rust-mozjs#1ec216a2577c03738fa11a78958bb2a0fd3f7fbd)",
|
||||
"layers 0.1.0 (git+https://github.com/servo/rust-layers#3737b00270644594a95896a2cd37ffca36d5bc5f)",
|
||||
|
@ -229,16 +229,16 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "glfw"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/glfw-rs?ref=servo#a15c2d04b8969aea653841d1d79e5fdf68de664b"
|
||||
source = "git+https://github.com/servo/glfw-rs?ref=servo#cec2861dd75eb721694b3176f94a276d77018bde"
|
||||
dependencies = [
|
||||
"glfw-sys 3.0.4 (git+https://github.com/servo/glfw?ref=cargo-3.0.4#65a2b4721276589d9de24f6a9999a2db37286cae)",
|
||||
"glfw-sys 3.0.4 (git+https://github.com/servo/glfw?ref=cargo-3.0.4#aa8e0d26cccdb4145f34c5a1724d7e48e0399674)",
|
||||
"semver 0.0.1 (git+https://github.com/rust-lang/semver#d04583a173395b76c1eaa15cc630a5f6f8f0ae10)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glfw-sys"
|
||||
version = "3.0.4"
|
||||
source = "git+https://github.com/servo/glfw?ref=cargo-3.0.4#65a2b4721276589d9de24f6a9999a2db37286cae"
|
||||
source = "git+https://github.com/servo/glfw?ref=cargo-3.0.4#aa8e0d26cccdb4145f34c5a1724d7e48e0399674"
|
||||
|
||||
[[package]]
|
||||
name = "glfw_app"
|
||||
|
@ -248,7 +248,7 @@ dependencies = [
|
|||
"cgl 0.0.1 (git+https://github.com/servo/rust-cgl#698c6c5409c1049ba5a7e0f7bdddf97f91dc4cf5)",
|
||||
"compositing 0.0.1",
|
||||
"geom 0.1.0 (git+https://github.com/servo/rust-geom#b001a76e907befaae1d0d6dd259418a22092da86)",
|
||||
"glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo#a15c2d04b8969aea653841d1d79e5fdf68de664b)",
|
||||
"glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo#cec2861dd75eb721694b3176f94a276d77018bde)",
|
||||
"layers 0.1.0 (git+https://github.com/servo/rust-layers#3737b00270644594a95896a2cd37ffca36d5bc5f)",
|
||||
"msg 0.0.1",
|
||||
"util 0.0.1",
|
||||
|
|
|
@ -9,12 +9,11 @@ use geom::size::TypedSize2D;
|
|||
use glfw_app;
|
||||
use libc::{c_int, c_void};
|
||||
use native;
|
||||
use servo;
|
||||
use servo::Browser;
|
||||
use servo_util::opts;
|
||||
use std::mem;
|
||||
use types::{cef_app_t, cef_main_args_t, cef_settings_t};
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn cef_initialize(args: *const cef_main_args_t,
|
||||
_settings: *mut cef_settings_t,
|
||||
|
@ -77,8 +76,10 @@ pub extern "C" fn cef_run_message_loop() {
|
|||
validate_display_list_geometry: false,
|
||||
});
|
||||
native::start(0, 0 as *const *const u8, proc() {
|
||||
let window = Some(glfw_app::create_window());
|
||||
servo::run(window);
|
||||
let window = glfw_app::create_window();
|
||||
let mut browser = Browser::new(Some(window.clone()));
|
||||
while browser.handle_event(window.wait_events()) {}
|
||||
browser.shutdown()
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -20,12 +20,17 @@ extern crate msg;
|
|||
extern crate time;
|
||||
extern crate util;
|
||||
|
||||
use compositing::windowing::WindowEvent;
|
||||
use geom::scale_factor::ScaleFactor;
|
||||
use std::rc::Rc;
|
||||
use window::Window;
|
||||
use util::opts;
|
||||
|
||||
mod window;
|
||||
pub mod window;
|
||||
|
||||
pub trait NestedEventLoopListener {
|
||||
fn handle_event_from_nested_event_loop(&mut self, event: WindowEvent) -> bool;
|
||||
}
|
||||
|
||||
pub fn create_window() -> Rc<Window> {
|
||||
// Initialize GLFW.
|
||||
|
|
|
@ -4,14 +4,18 @@
|
|||
|
||||
//! A windowing implementation using GLFW.
|
||||
|
||||
use NestedEventLoopListener;
|
||||
|
||||
use alert::{Alert, AlertMethods};
|
||||
use compositing::windowing::{WindowEvent, WindowMethods};
|
||||
use compositing::compositor_task::{mod, CompositorProxy, CompositorReceiver};
|
||||
use compositing::windowing::{Forward, Back};
|
||||
use compositing::windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent};
|
||||
use compositing::windowing::{MouseWindowEventClass, MouseWindowMoveEventClass, ScrollWindowEvent};
|
||||
use compositing::windowing::{ZoomWindowEvent, PinchZoomWindowEvent, NavigationWindowEvent};
|
||||
use compositing::windowing::{FinishedWindowEvent, QuitWindowEvent, MouseWindowClickEvent};
|
||||
use compositing::windowing::{MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
|
||||
use compositing::windowing::{RefreshWindowEvent, Forward, Back};
|
||||
use compositing::windowing::{MouseWindowClickEvent, MouseWindowMouseDownEvent};
|
||||
use compositing::windowing::{MouseWindowEventClass, MouseWindowMoveEventClass};
|
||||
use compositing::windowing::{MouseWindowMouseUpEvent, RefreshWindowEvent};
|
||||
use compositing::windowing::{NavigationWindowEvent, ScrollWindowEvent, ZoomWindowEvent};
|
||||
use compositing::windowing::{PinchZoomWindowEvent, QuitWindowEvent};
|
||||
use compositing::windowing::{WindowEvent, WindowMethods, FinishedWindowEvent};
|
||||
use geom::point::{Point2D, TypedPoint2D};
|
||||
use geom::scale_factor::ScaleFactor;
|
||||
use geom::size::TypedSize2D;
|
||||
|
@ -19,8 +23,8 @@ use glfw::{mod, Context};
|
|||
use layers::geometry::DevicePixel;
|
||||
use layers::platform::surface::NativeGraphicsMetadata;
|
||||
use libc::c_int;
|
||||
use msg::compositor_msg::{IdleRenderState, RenderState, RenderingRenderState};
|
||||
use msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayout, ReadyState};
|
||||
use msg::compositor_msg::{IdleRenderState, RenderState, RenderingRenderState};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::comm::Receiver;
|
||||
use std::rc::Rc;
|
||||
|
@ -84,12 +88,50 @@ impl Window {
|
|||
window.glfw_window.set_cursor_pos_polling(true);
|
||||
window.glfw_window.set_scroll_polling(true);
|
||||
|
||||
let wrapped_window = Rc::new(window);
|
||||
glfw.set_swap_interval(1);
|
||||
|
||||
wrapped_window
|
||||
Rc::new(window)
|
||||
}
|
||||
|
||||
pub fn wait_events(&self) -> WindowEvent {
|
||||
{
|
||||
let mut event_queue = self.event_queue.borrow_mut();
|
||||
if !event_queue.is_empty() {
|
||||
return event_queue.remove(0).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
self.glfw.wait_events();
|
||||
for (_, event) in glfw::flush_messages(&self.events) {
|
||||
self.handle_window_event(&self.glfw_window, event);
|
||||
}
|
||||
|
||||
if self.glfw_window.should_close() {
|
||||
QuitWindowEvent
|
||||
} else {
|
||||
self.event_queue.borrow_mut().remove(0).unwrap_or(IdleWindowEvent)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn set_nested_event_loop_listener(
|
||||
&self,
|
||||
listener: *mut NestedEventLoopListener + 'static) {
|
||||
self.glfw_window.set_refresh_polling(false);
|
||||
glfw::ffi::glfwSetWindowRefreshCallback(self.glfw_window.ptr, Some(on_refresh));
|
||||
glfw::ffi::glfwSetFramebufferSizeCallback(self.glfw_window.ptr, Some(on_framebuffer_size));
|
||||
g_nested_event_loop_listener = Some(listener)
|
||||
}
|
||||
|
||||
pub unsafe fn remove_nested_event_loop_listener(&self) {
|
||||
glfw::ffi::glfwSetWindowRefreshCallback(self.glfw_window.ptr, None);
|
||||
glfw::ffi::glfwSetFramebufferSizeCallback(self.glfw_window.ptr, None);
|
||||
self.glfw_window.set_refresh_polling(true);
|
||||
g_nested_event_loop_listener = None
|
||||
}
|
||||
}
|
||||
|
||||
static mut g_nested_event_loop_listener: Option<*mut NestedEventLoopListener + 'static> = None;
|
||||
|
||||
impl WindowMethods for Window {
|
||||
/// Returns the size of the window in hardware pixels.
|
||||
fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint> {
|
||||
|
@ -108,26 +150,6 @@ impl WindowMethods for Window {
|
|||
self.glfw_window.swap_buffers();
|
||||
}
|
||||
|
||||
fn recv(&self) -> WindowEvent {
|
||||
{
|
||||
let mut event_queue = self.event_queue.borrow_mut();
|
||||
if !event_queue.is_empty() {
|
||||
return event_queue.remove(0).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
self.glfw.poll_events();
|
||||
for (_, event) in glfw::flush_messages(&self.events) {
|
||||
self.handle_window_event(&self.glfw_window, event);
|
||||
}
|
||||
|
||||
if self.glfw_window.should_close() {
|
||||
QuitWindowEvent
|
||||
} else {
|
||||
self.event_queue.borrow_mut().remove(0).unwrap_or(IdleWindowEvent)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the ready state.
|
||||
fn set_ready_state(&self, ready_state: ReadyState) {
|
||||
self.ready_state.set(ready_state);
|
||||
|
@ -169,6 +191,15 @@ impl WindowMethods for Window {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_compositor_channel(_: &Option<Rc<Window>>)
|
||||
-> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) {
|
||||
let (sender, receiver) = channel();
|
||||
(box GlfwCompositorProxy {
|
||||
sender: sender,
|
||||
} as Box<CompositorProxy+Send>,
|
||||
box receiver as Box<CompositorReceiver>)
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
@ -219,7 +250,6 @@ impl Window {
|
|||
self.scroll_window(dx, dy);
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
@ -346,3 +376,45 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
struct GlfwCompositorProxy {
|
||||
sender: Sender<compositor_task::Msg>,
|
||||
}
|
||||
|
||||
impl CompositorProxy for GlfwCompositorProxy {
|
||||
fn send(&mut self, msg: compositor_task::Msg) {
|
||||
// Send a message and kick the OS event loop awake.
|
||||
self.sender.send(msg);
|
||||
glfw::Glfw::post_empty_event()
|
||||
}
|
||||
fn clone_compositor_proxy(&self) -> Box<CompositorProxy+Send> {
|
||||
box GlfwCompositorProxy {
|
||||
sender: self.sender.clone(),
|
||||
} as Box<CompositorProxy+Send>
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn on_refresh(_glfw_window: *mut glfw::ffi::GLFWwindow) {
|
||||
unsafe {
|
||||
match g_nested_event_loop_listener {
|
||||
None => {}
|
||||
Some(listener) => {
|
||||
(*listener).handle_event_from_nested_event_loop(RefreshWindowEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn on_framebuffer_size(_glfw_window: *mut glfw::ffi::GLFWwindow,
|
||||
width: c_int,
|
||||
height: c_int) {
|
||||
unsafe {
|
||||
match g_nested_event_loop_listener {
|
||||
None => {}
|
||||
Some(listener) => {
|
||||
let size = TypedSize2D(width as uint, height as uint);
|
||||
(*listener).handle_event_from_nested_event_loop(ResizeWindowEvent(size));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
51
src/lib.rs
51
src/lib.rs
|
@ -30,10 +30,11 @@ extern crate native;
|
|||
extern crate rustrt;
|
||||
extern crate url;
|
||||
|
||||
use compositing::CompositorEventListener;
|
||||
use compositing::windowing::{WindowEvent, WindowMethods};
|
||||
|
||||
#[cfg(not(test))]
|
||||
use compositing::{CompositorChan, CompositorTask, Constellation};
|
||||
#[cfg(not(test))]
|
||||
use compositing::windowing::WindowMethods;
|
||||
use compositing::{CompositorProxy, CompositorTask, Constellation};
|
||||
#[cfg(not(test))]
|
||||
use servo_msg::constellation_msg::{ConstellationChan, InitLoadUrlMsg};
|
||||
#[cfg(not(test))]
|
||||
|
@ -63,8 +64,14 @@ use std::rc::Rc;
|
|||
#[cfg(not(test))]
|
||||
use std::task::TaskBuilder;
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub fn run<Window: WindowMethods>(window: Option<Rc<Window>>) {
|
||||
pub struct Browser<Window> {
|
||||
pool: green::SchedPool,
|
||||
compositor: Box<CompositorEventListener + 'static>,
|
||||
}
|
||||
|
||||
impl<Window> Browser<Window> where Window: WindowMethods + 'static {
|
||||
#[cfg(not(test))]
|
||||
pub fn new(window: Option<Rc<Window>>) -> Browser<Window> {
|
||||
::servo_util::opts::set_experimental_enabled(opts::get().enable_experimental);
|
||||
let opts = opts::get();
|
||||
RegisterBindings::RegisterProxyHandlers();
|
||||
|
@ -72,21 +79,25 @@ pub fn run<Window: WindowMethods>(window: Option<Rc<Window>>) {
|
|||
let mut pool_config = green::PoolConfig::new();
|
||||
pool_config.event_loop_factory = rustuv::event_loop;
|
||||
let mut pool = green::SchedPool::new(pool_config);
|
||||
let shared_task_pool = TaskPool::new(8);
|
||||
|
||||
let (compositor_port, compositor_chan) = CompositorChan::new();
|
||||
let (compositor_proxy, compositor_receiver) =
|
||||
WindowMethods::create_compositor_channel(&window);
|
||||
let time_profiler_chan = TimeProfiler::create(opts.time_profiler_period);
|
||||
let memory_profiler_chan = MemoryProfiler::create(opts.memory_profiler_period);
|
||||
let devtools_chan = opts.devtools_port.map(|port| {
|
||||
devtools::start_server(port)
|
||||
});
|
||||
|
||||
let opts_clone = opts.clone();
|
||||
let time_profiler_chan_clone = time_profiler_chan.clone();
|
||||
let shared_task_pool = TaskPool::new(8);
|
||||
|
||||
let (result_chan, result_port) = channel();
|
||||
let compositor_proxy_for_constellation = compositor_proxy.clone_compositor_proxy();
|
||||
TaskBuilder::new()
|
||||
.green(&mut pool)
|
||||
.spawn(proc() {
|
||||
let opts = &opts_clone;
|
||||
// Create a Servo instance.
|
||||
let resource_task = new_resource_task(opts.user_agent.clone());
|
||||
// If we are emitting an output file, then we need to block on
|
||||
|
@ -100,7 +111,7 @@ pub fn run<Window: WindowMethods>(window: Option<Rc<Window>>) {
|
|||
let font_cache_task = FontCacheTask::new(resource_task.clone());
|
||||
let constellation_chan = Constellation::<layout::layout_task::LayoutTask,
|
||||
script::script_task::ScriptTask>::start(
|
||||
compositor_chan,
|
||||
compositor_proxy_for_constellation,
|
||||
resource_task,
|
||||
image_cache_task,
|
||||
font_cache_task,
|
||||
|
@ -128,12 +139,30 @@ pub fn run<Window: WindowMethods>(window: Option<Rc<Window>>) {
|
|||
let constellation_chan = result_port.recv();
|
||||
|
||||
debug!("preparing to enter main loop");
|
||||
CompositorTask::create(window,
|
||||
compositor_port,
|
||||
let compositor = CompositorTask::create(window,
|
||||
compositor_proxy,
|
||||
compositor_receiver,
|
||||
constellation_chan,
|
||||
time_profiler_chan,
|
||||
memory_profiler_chan);
|
||||
|
||||
pool.shutdown();
|
||||
Browser {
|
||||
pool: pool,
|
||||
compositor: compositor,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_event(&mut self, event: WindowEvent) -> bool {
|
||||
self.compositor.handle_event(event)
|
||||
}
|
||||
|
||||
pub fn repaint_synchronously(&mut self) {
|
||||
self.compositor.repaint_synchronously()
|
||||
}
|
||||
|
||||
pub fn shutdown(mut self) {
|
||||
self.compositor.shutdown();
|
||||
self.pool.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
71
src/main.rs
71
src/main.rs
|
@ -9,10 +9,13 @@
|
|||
|
||||
extern crate servo;
|
||||
extern crate native;
|
||||
extern crate time;
|
||||
extern crate "util" as servo_util;
|
||||
|
||||
#[cfg(not(test),not(target_os="android"))]
|
||||
extern crate glfw_app;
|
||||
#[cfg(not(test),not(target_os="android"))]
|
||||
extern crate compositing;
|
||||
|
||||
#[cfg(not(test),not(target_os="android"))]
|
||||
use servo_util::opts;
|
||||
|
@ -21,11 +24,18 @@ use servo_util::opts;
|
|||
use servo_util::rtinstrument;
|
||||
|
||||
#[cfg(not(test),not(target_os="android"))]
|
||||
use servo::run;
|
||||
use servo::Browser;
|
||||
#[cfg(not(test),not(target_os="android"))]
|
||||
use compositing::windowing::{IdleWindowEvent, ResizeWindowEvent, WindowEvent};
|
||||
|
||||
#[cfg(not(test),not(target_os="android"))]
|
||||
use std::os;
|
||||
|
||||
#[cfg(not(test),not(target_os="android"))]
|
||||
struct BrowserWrapper {
|
||||
browser: Browser<glfw_app::window::Window>,
|
||||
}
|
||||
|
||||
#[cfg(not(test), not(target_os="android"))]
|
||||
#[start]
|
||||
#[allow(dead_code)]
|
||||
|
@ -37,7 +47,46 @@ fn start(argc: int, argv: *const *const u8) -> int {
|
|||
} else {
|
||||
Some(glfw_app::create_window())
|
||||
};
|
||||
run(window);
|
||||
|
||||
let mut browser = BrowserWrapper {
|
||||
browser: Browser::new(window.clone()),
|
||||
};
|
||||
|
||||
match window {
|
||||
None => {}
|
||||
Some(ref window) => {
|
||||
unsafe {
|
||||
window.set_nested_event_loop_listener(&mut browser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
let should_continue = match window {
|
||||
None => browser.browser.handle_event(IdleWindowEvent),
|
||||
Some(ref window) => {
|
||||
let event = window.wait_events();
|
||||
browser.browser.handle_event(event)
|
||||
}
|
||||
};
|
||||
if !should_continue {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
match window {
|
||||
None => {}
|
||||
Some(ref window) => {
|
||||
unsafe {
|
||||
window.remove_nested_event_loop_listener();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let BrowserWrapper {
|
||||
browser
|
||||
} = browser;
|
||||
browser.shutdown();
|
||||
|
||||
rtinstrument::teardown();
|
||||
}
|
||||
|
@ -46,3 +95,21 @@ fn start(argc: int, argv: *const *const u8) -> int {
|
|||
|
||||
#[cfg(not(test), target_os="android")]
|
||||
fn main() {}
|
||||
|
||||
#[cfg(not(test),not(target_os="android"))]
|
||||
impl glfw_app::NestedEventLoopListener for BrowserWrapper {
|
||||
fn handle_event_from_nested_event_loop(&mut self, event: WindowEvent) -> bool {
|
||||
let is_resize = match event {
|
||||
ResizeWindowEvent(..) => true,
|
||||
_ => false,
|
||||
};
|
||||
if !self.browser.handle_event(event) {
|
||||
return false
|
||||
}
|
||||
if is_resize {
|
||||
self.browser.repaint_synchronously()
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit cec2861dd75eb721694b3176f94a276d77018bde
|
||||
Subproject commit 8edf667fbf289d909c7d1bdde03acf6d78168674
|
Loading…
Add table
Add a link
Reference in a new issue