mirror of
https://github.com/servo/servo.git
synced 2025-10-04 02:29:12 +01:00
196 lines
6.3 KiB
Rust
196 lines
6.3 KiB
Rust
/* 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()
|
|
}
|
|
|
|
pub fn gl_version() -> glutin::GlRequest {
|
|
if opts::get().angle {
|
|
glutin::GlRequest::Specific(glutin::Api::OpenGlEs, (3, 0))
|
|
} else {
|
|
glutin::GlRequest::GlThenGles {
|
|
opengl_version: (3, 2),
|
|
opengles_version: (3, 0),
|
|
}
|
|
}
|
|
}
|