Skip to content
Permalink
Browse files

Refactoring of the Glutin port in preparation of the compositor refac…

…toring.
  • Loading branch information...
paulrouget committed Apr 17, 2019
1 parent d58ea97 commit 21ed7653f491d9e378aa7a334b40e7327f4bfc90
@@ -1,6 +1,6 @@
[workspace]
members = [
"ports/servo",
"ports/glutin",
"ports/libsimpleservo/capi/",
"ports/libsimpleservo/jniapi/",
"ports/libmlservo/",
@@ -103,7 +103,7 @@ impl FrameTreeId {
enum LayerPixel {}

/// NB: Never block on the constellation, because sometimes the constellation blocks on us.
pub struct IOCompositor<Window: WindowMethods> {
pub struct IOCompositor<Window: WindowMethods + ?Sized> {
/// The application window.
pub window: Rc<Window>,

@@ -258,7 +258,7 @@ enum CompositeTarget {
PngFile,
}

impl<Window: WindowMethods> IOCompositor<Window> {
impl<Window: WindowMethods + ?Sized> IOCompositor<Window> {
fn new(window: Rc<Window>, state: InitialCompositorState) -> Self {
let composite_target = match opts::get().output_file {
Some(_) => CompositeTarget::PngFile,
@@ -148,15 +148,18 @@ pub trait WindowMethods {
/// Return the GL function pointer trait.
#[cfg(feature = "gl")]
fn gl(&self) -> Rc<dyn gl::Gl>;
/// Returns a thread-safe object to wake up the window's event loop.
fn create_event_loop_waker(&self) -> Box<dyn EventLoopWaker>;
/// Get the coordinates of the native window, the screen and the framebuffer.
fn get_coordinates(&self) -> EmbedderCoordinates;
/// Set whether the application is currently animating.
/// Typically, when animations are active, the window
/// will want to avoid blocking on UI events, and just
/// run the event loop at the vsync interval.
fn set_animation_state(&self, _state: AnimationState);
}

pub trait EmbedderMethods {
/// Returns a thread-safe object to wake up the window's event loop.
fn create_event_loop_waker(&self) -> Box<dyn EventLoopWaker>;
/// Register services with a VRServiceManager.
fn register_vr_services(
&self,
@@ -67,7 +67,7 @@ use canvas::webgl_thread::WebGLThreads;
use compositing::compositor_thread::{
CompositorProxy, CompositorReceiver, InitialCompositorState, Msg,
};
use compositing::windowing::{WindowEvent, WindowMethods};
use compositing::windowing::{EmbedderMethods, WindowEvent, WindowMethods};
use compositing::{CompositingReason, IOCompositor, ShutdownState};
#[cfg(all(
not(target_os = "windows"),
@@ -148,7 +148,7 @@ type MediaBackend = media_platform::MediaBackend;
/// application Servo is embedded in. Clients then create an event
/// loop to pump messages between the embedding application and
/// various browser components.
pub struct Servo<Window: WindowMethods + 'static> {
pub struct Servo<Window: WindowMethods + 'static + ?Sized> {
compositor: IOCompositor<Window>,
constellation_chan: Sender<ConstellationMsg>,
embedder_receiver: EmbedderReceiver,
@@ -197,9 +197,9 @@ impl webrender_api::RenderNotifier for RenderNotifier {

impl<Window> Servo<Window>
where
Window: WindowMethods + 'static,
Window: WindowMethods + 'static + ?Sized,
{
pub fn new(window: Rc<Window>) -> Servo<Window> {
pub fn new(embedder: Box<EmbedderMethods>, window: Rc<Window>) -> Servo<Window> {
// Global configuration options, parsed from the command line.
let opts = opts::get();

@@ -218,9 +218,9 @@ where
// messages to client may need to pump a platform-specific event loop
// to deliver the message.
let (compositor_proxy, compositor_receiver) =
create_compositor_channel(window.create_event_loop_waker());
create_compositor_channel(embedder.create_event_loop_waker());
let (embedder_proxy, embedder_receiver) =
create_embedder_channel(window.create_event_loop_waker());
create_embedder_channel(embedder.create_event_loop_waker());
let time_profiler_chan = profile_time::Profiler::create(
&opts.time_profiling,
opts.time_profiler_trace_path.clone(),
@@ -288,7 +288,7 @@ where
let webvr_services = if pref!(dom.webvr.enabled) {
let mut services = VRServiceManager::new();
services.register_defaults();
window.register_vr_services(&mut services, &mut webvr_heartbeats);
embedder.register_vr_services(&mut services, &mut webvr_heartbeats);
Some(services)
} else {
None
@@ -17,8 +17,9 @@ cd "$(git rev-parse --show-toplevel)"
PATHS=(
"components/compositing/compositor.rs"
"components/constellation/"
"ports/servo/glutin_app/mod.rs"
"ports/servo/glutin_app/window.rs"
"ports/glutin/headed_window.rs"
"ports/glutin/headless_window.rs"
"ports/glutin/embedder.rs"
)

# Make sure the paths exist
File renamed without changes.
@@ -0,0 +1,195 @@
/* 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 https://mozilla.org/MPL/2.0/. */

//! Application entry point, runs the event loop.

use crate::browser::Browser;
use crate::embedder::EmbedderCallbacks;
use crate::window_trait::WindowPortsMethods;
use crate::events_loop::EventsLoop;
use crate::{headed_window, headless_window};
use servo::compositing::windowing::WindowEvent;
use servo::config::opts::{self, parse_url_or_filename};
use servo::servo_config::pref;
use servo::servo_url::ServoUrl;
use servo::{BrowserId, Servo};
use std::cell::{Cell, RefCell};
use std::env;
use std::mem;
use std::rc::Rc;

pub struct App {
events_loop: Rc<RefCell<EventsLoop>>,
window: Rc<WindowPortsMethods>,
servo: RefCell<Servo<WindowPortsMethods>>,
browser: RefCell<Browser<WindowPortsMethods>>,
event_queue: RefCell<Vec<WindowEvent>>,
suspended: Cell<bool>,
}

impl App {
pub fn run() {
let events_loop = EventsLoop::new(opts::get().headless);

// Implements window methods, used by compositor.
let window = if opts::get().headless {
headless_window::Window::new(opts::get().initial_window_size)
} else {
headed_window::Window::new(opts::get().initial_window_size, events_loop.borrow().as_winit())
};

// Implements embedder methods, used by libservo and constellation.
let embedder = Box::new(EmbedderCallbacks::new(
events_loop.clone(),
window.gl(),
));

// Handle browser state.
let browser = Browser::new(window.clone());

let mut servo = Servo::new(embedder, window.clone());
let browser_id = BrowserId::new();
servo.handle_events(vec![WindowEvent::NewBrowser(get_default_url(), browser_id)]);
servo.setup_logging();

let app = App {
event_queue: RefCell::new(vec![]),
events_loop,
window: window,
browser: RefCell::new(browser),
servo: RefCell::new(servo),
suspended: Cell::new(false),
};

app.run_loop();
}

fn get_events(&self) -> Vec<WindowEvent> {
mem::replace(&mut *self.event_queue.borrow_mut(), Vec::new())
}

fn has_events(&self) -> bool {
!self.event_queue.borrow().is_empty() || self.window.has_events()
}

fn winit_event_to_servo_event(&self, event: glutin::Event) {
match event {
// App level events
glutin::Event::Suspended(suspended) => {
self.suspended.set(suspended);
if !suspended {
self.event_queue.borrow_mut().push(WindowEvent::Idle);
}
},
glutin::Event::Awakened => {
self.event_queue.borrow_mut().push(WindowEvent::Idle);
},
glutin::Event::DeviceEvent { .. } => {},

// Window level events
glutin::Event::WindowEvent {
window_id, event, ..
} => {
if Some(window_id) != self.window.id() {
warn!("Got an event from unknown window");
} else {
self.window.winit_event_to_servo_event(event);
}
},
}
}

fn run_loop(self) {
let mut stop = false;
loop {
let mut events_loop = self.events_loop.borrow_mut();
if self.window.is_animating() && !self.suspended.get() {
// We block on compositing (self.handle_events() ends up calling swap_buffers)
events_loop.poll_events(|e| {
self.winit_event_to_servo_event(e);
});
stop = self.handle_events();
} else {
// We block on winit's event loop (window events)
events_loop.run_forever(|e| {
self.winit_event_to_servo_event(e);
if self.has_events() && !self.suspended.get() {
stop = self.handle_events();
}
if stop || self.window.is_animating() && !self.suspended.get() {
glutin::ControlFlow::Break
} else {
glutin::ControlFlow::Continue
}
});
}
if stop {
break;
}
}

self.servo.into_inner().deinit()
}

fn handle_events(&self) -> bool {
let mut browser = self.browser.borrow_mut();
let mut servo = self.servo.borrow_mut();

let win_events = self.window.get_events();

// FIXME: this could be handled by Servo. We don't need
// a repaint_synchronously function exposed.
let need_resize = win_events.iter().any(|e| match *e {
WindowEvent::Resize => true,
_ => false,
});

let mut app_events = self.get_events();
app_events.extend(win_events);

browser.handle_window_events(app_events);

let mut servo_events = servo.get_events();
loop {
browser.handle_servo_events(servo_events);
servo.handle_events(browser.get_events());
if browser.shutdown_requested() {
return true;
}
servo_events = servo.get_events();
if servo_events.is_empty() {
break;
}
}

if need_resize {
servo.repaint_synchronously();
}
false
}
}

fn get_default_url() -> ServoUrl {
// If the url is not provided, we fallback to the homepage in prefs,
// or a blank page in case the homepage is not set either.
let cwd = env::current_dir().unwrap();
let cmdline_url = opts::get().url.clone();
let pref_url = {
let homepage_url = pref!(shell.homepage);
parse_url_or_filename(&cwd, &homepage_url).ok()
};
let blank_url = ServoUrl::parse("about:blank").ok();

cmdline_url.or(pref_url).or(blank_url).unwrap()
}

#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
pub fn gl_version() -> glutin::GlRequest {
glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 2))
}

#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
pub fn gl_version() -> glutin::GlRequest {
glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (3, 0))
}
@@ -2,13 +2,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::glutin_app::keyutils::{CMD_OR_CONTROL, CMD_OR_ALT};
use crate::glutin_app::window::{Window, LINE_HEIGHT};
use crate::keyutils::{CMD_OR_ALT, CMD_OR_CONTROL};
use crate::window_trait::{WindowPortsMethods, LINE_HEIGHT};
use euclid::{TypedPoint2D, TypedVector2D};
use keyboard_types::{Key, KeyboardEvent, Modifiers, ShortcutMatcher};
use servo::compositing::windowing::{WebRenderDebugOption, WindowEvent};
use servo::embedder_traits::{EmbedderMsg, FilterPattern};
use servo::msg::constellation_msg::{TopLevelBrowsingContextId as BrowserId};
use servo::msg::constellation_msg::TopLevelBrowsingContextId as BrowserId;
use servo::msg::constellation_msg::TraversalDirection;
use servo::net_traits::pub_domains::is_reg_domain;
use servo::script_traits::TouchEventType;
@@ -25,7 +25,7 @@ use std::thread;
use std::time::Duration;
use tinyfiledialogs::{self, MessageBoxIcon};

pub struct Browser {
pub struct Browser<Window: WindowPortsMethods + ?Sized> {
current_url: Option<ServoUrl>,
/// id of the top level browsing context. It is unique as tabs
/// are not supported yet. None until created.
@@ -52,8 +52,11 @@ enum LoadingState {
Loaded,
}

impl Browser {
pub fn new(window: Rc<Window>) -> Browser {
impl<Window> Browser<Window>
where
Window: WindowPortsMethods + ?Sized,
{
pub fn new(window: Rc<Window>) -> Browser<Window> {
Browser {
title: None,
current_url: None,
@@ -126,7 +129,9 @@ impl Browser {
.and_then(|s| s.parse().ok())
.unwrap_or(10);
self.event_queue.push(WindowEvent::ToggleSamplingProfiler(
Duration::from_millis(rate), Duration::from_secs(duration)));
Duration::from_millis(rate),
Duration::from_secs(duration),
));
})
.shortcut(Modifiers::CONTROL, Key::F9, || {
self.event_queue.push(WindowEvent::CaptureWebRender)
@@ -403,14 +408,12 @@ impl Browser {
debug!("HideIME received");
},
EmbedderMsg::ReportProfile(bytes) => {
let filename = env::var("PROFILE_OUTPUT")
.unwrap_or("samples.json".to_string());
let result = File::create(&filename)
.and_then(|mut f| f.write_all(&bytes));
let filename = env::var("PROFILE_OUTPUT").unwrap_or("samples.json".to_string());
let result = File::create(&filename).and_then(|mut f| f.write_all(&bytes));
if let Err(e) = result {
error!("Failed to store profile: {}", e);
}
}
},
}
}
}
File renamed without changes.
Oops, something went wrong.

0 comments on commit 21ed765

Please sign in to comment.
You can’t perform that action at this time.