aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml2
-rw-r--r--components/compositing/compositor.rs4
-rw-r--r--components/compositing/windowing.rs7
-rw-r--r--components/servo/lib.rs14
-rwxr-xr-xetc/ci/check_no_panic.sh5
-rw-r--r--ports/glutin/Cargo.toml (renamed from ports/servo/Cargo.toml)0
-rw-r--r--ports/glutin/app.rs195
-rw-r--r--ports/glutin/browser.rs (renamed from ports/servo/browser.rs)27
-rw-r--r--ports/glutin/build.rs (renamed from ports/servo/build.rs)0
-rw-r--r--ports/glutin/embedder.rs71
-rw-r--r--ports/glutin/events_loop.rs98
-rw-r--r--ports/glutin/headed_window.rs533
-rw-r--r--ports/glutin/headless_window.rs200
-rw-r--r--ports/glutin/keyutils.rs (renamed from ports/servo/glutin_app/keyutils.rs)2
-rw-r--r--ports/glutin/main.rs (renamed from ports/servo/main.rs)8
-rw-r--r--ports/glutin/main2.rs (renamed from ports/servo/non_android_main.rs)141
-rw-r--r--ports/glutin/platform/macos/Info.plist (renamed from ports/servo/platform/macos/Info.plist)0
-rw-r--r--ports/glutin/platform/macos/count_threads.c (renamed from ports/servo/platform/macos/count_threads.c)0
-rw-r--r--ports/glutin/platform/macos/mod.rs (renamed from ports/servo/platform/macos/mod.rs)15
-rw-r--r--ports/glutin/platform/windows/servo.exe.manifest (renamed from ports/servo/platform/windows/servo.exe.manifest)0
-rw-r--r--ports/glutin/resources.rs (renamed from ports/servo/resources.rs)0
-rw-r--r--ports/glutin/skia_symbols.rs55
-rw-r--r--ports/glutin/window_trait.rs29
-rw-r--r--ports/libsimpleservo/api/src/lib.rs58
-rw-r--r--ports/servo/glutin_app/mod.rs20
-rw-r--r--ports/servo/glutin_app/window.rs854
-rw-r--r--python/servo/build_commands.py2
-rw-r--r--python/servo/command_base.py10
-rw-r--r--python/servo/post_build_commands.py2
-rw-r--r--python/servo/testing_commands.py2
30 files changed, 1287 insertions, 1067 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 0cfd8e31053..fd6954186b7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[workspace]
members = [
- "ports/servo",
+ "ports/glutin",
"ports/libsimpleservo/capi/",
"ports/libsimpleservo/jniapi/",
"ports/libmlservo/",
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs
index 6cbf1d35160..2b1322bfc63 100644
--- a/components/compositing/compositor.rs
+++ b/components/compositing/compositor.rs
@@ -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,
diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs
index 6105531bf85..fa9f8c481fb 100644
--- a/components/compositing/windowing.rs
+++ b/components/compositing/windowing.rs
@@ -148,8 +148,6 @@ 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.
@@ -157,6 +155,11 @@ pub trait WindowMethods {
/// 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,
diff --git a/components/servo/lib.rs b/components/servo/lib.rs
index d919613c9c0..d3d8d8eb1cb 100644
--- a/components/servo/lib.rs
+++ b/components/servo/lib.rs
@@ -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
diff --git a/etc/ci/check_no_panic.sh b/etc/ci/check_no_panic.sh
index 17343b7fbd3..be59f5ddef1 100755
--- a/etc/ci/check_no_panic.sh
+++ b/etc/ci/check_no_panic.sh
@@ -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
diff --git a/ports/servo/Cargo.toml b/ports/glutin/Cargo.toml
index 4edbef5fd85..4edbef5fd85 100644
--- a/ports/servo/Cargo.toml
+++ b/ports/glutin/Cargo.toml
diff --git a/ports/glutin/app.rs b/ports/glutin/app.rs
new file mode 100644
index 00000000000..59d37fcd246
--- /dev/null
+++ b/ports/glutin/app.rs
@@ -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))
+}
diff --git a/ports/servo/browser.rs b/ports/glutin/browser.rs
index 6e74b462399..e12d283fc4e 100644
--- a/ports/servo/browser.rs
+++ b/ports/glutin/browser.rs
@@ -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);
}
- }
+ },
}
}
}
diff --git a/ports/servo/build.rs b/ports/glutin/build.rs
index 9da6e9e53bd..9da6e9e53bd 100644
--- a/ports/servo/build.rs
+++ b/ports/glutin/build.rs
diff --git a/ports/glutin/embedder.rs b/ports/glutin/embedder.rs
new file mode 100644
index 00000000000..c5a04c874e5
--- /dev/null
+++ b/ports/glutin/embedder.rs
@@ -0,0 +1,71 @@
+/* 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/. */
+
+//! Implements the global methods required by Servo (not window/gl/compositor related).
+
+use crate::app;
+use crate::events_loop::EventsLoop;
+use gleam::gl;
+use glutin;
+use glutin::dpi::LogicalSize;
+use glutin::{ContextBuilder, GlWindow};
+use rust_webvr::GlWindowVRService;
+use servo::compositing::windowing::EmbedderMethods;
+use servo::embedder_traits::EventLoopWaker;
+use servo::servo_config::{opts, pref};
+use servo::webvr::VRServiceManager;
+use servo::webvr_traits::WebVRMainThreadHeartbeat;
+use std::cell::RefCell;
+use std::rc::Rc;
+
+pub struct EmbedderCallbacks {
+ events_loop: Rc<RefCell<EventsLoop>>,
+ gl: Rc<dyn gl::Gl>,
+}
+
+impl EmbedderCallbacks {
+ pub fn new(events_loop: Rc<RefCell<EventsLoop>>, gl: Rc<gl::Gl>) -> EmbedderCallbacks {
+ EmbedderCallbacks { events_loop, gl }
+ }
+}
+
+impl EmbedderMethods for EmbedderCallbacks {
+ fn create_event_loop_waker(&self) -> Box<dyn EventLoopWaker> {
+ self.events_loop.borrow().create_event_loop_waker()
+ }
+
+ fn register_vr_services(
+ &self,
+ services: &mut VRServiceManager,
+ heartbeats: &mut Vec<Box<WebVRMainThreadHeartbeat>>,
+ ) {
+ if !opts::get().headless {
+ if pref!(dom.webvr.test) {
+ warn!("Creating test VR display");
+ // This is safe, because register_vr_services is called from the main thread.
+ let name = String::from("Test VR Display");
+ let size = opts::get().initial_window_size.to_f64();
+ let size = LogicalSize::new(size.width, size.height);
+ let window_builder = glutin::WindowBuilder::new()
+ .with_title(name.clone())
+ .with_dimensions(size)
+ .with_visibility(false)
+ .with_multitouch();
+ let context_builder = ContextBuilder::new()
+ .with_gl(app::gl_version())
+ .with_vsync(false); // Assume the browser vsync is the same as the test VR window vsync
+ let gl_window =
+ GlWindow::new(window_builder, context_builder, &*self.events_loop.borrow().as_winit())
+ .expect("Failed to create window.");
+ let gl = self.gl.clone();
+ let (service, heartbeat) = GlWindowVRService::new(name, gl_window, gl);
+
+ services.register(Box::new(service));
+ heartbeats.push(Box::new(heartbeat));
+ }
+ } else {
+ // FIXME: support headless mode
+ }
+ }
+}
diff --git a/ports/glutin/events_loop.rs b/ports/glutin/events_loop.rs
new file mode 100644
index 00000000000..fbe3cd57017
--- /dev/null
+++ b/ports/glutin/events_loop.rs
@@ -0,0 +1,98 @@
+/* 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/. */
+
+//! An event loop implementation that works in headless mode.
+
+
+use glutin;
+use servo::embedder_traits::EventLoopWaker;
+use std::sync::Arc;
+use std::rc::Rc;
+use std::cell::RefCell;
+use std::thread;
+use std::time;
+
+pub struct EventsLoop(Option<glutin::EventsLoop>);
+
+impl EventsLoop {
+ // Ideally, we could use the winit event loop in both modes,
+ // but on Linux, the event loop requires a X11 server.
+ #[cfg(not(target_os = "linux"))]
+ pub fn new(_headless: bool) -> Rc<RefCell<EventsLoop>> {
+ Rc::new(RefCell::new(EventsLoop(Some(glutin::EventsLoop::new()))))
+ }
+ #[cfg(target_os = "linux")]
+ pub fn new(headless: bool) -> Rc<RefCell<EventsLoop>> {
+ let events_loop = if headless {
+ None
+ } else {
+ Some(glutin::EventsLoop::new())
+ };
+ Rc::new(RefCell::new(EventsLoop(events_loop)))
+ }
+}
+
+impl EventsLoop {
+ pub fn create_event_loop_waker(&self) -> Box<dyn EventLoopWaker> {
+ if let Some(ref events_loop) = self.0 {
+ Box::new(HeadedEventLoopWaker::new(&events_loop))
+ } else {
+ Box::new(HeadlessEventLoopWaker)
+ }
+ }
+ pub fn as_winit(&self) -> &glutin::EventsLoop {
+ &self.0.as_ref().expect("Can't access winit event loop while using the fake headless event loop")
+ }
+ pub fn poll_events<F>(&mut self, callback: F) where F: FnMut(glutin::Event) {
+ if let Some(ref mut events_loop) = self.0 {
+ events_loop.poll_events(callback);
+ } else {
+ self.sleep();
+ }
+ }
+ pub fn run_forever<F>(&mut self, mut callback: F) where F: FnMut(glutin::Event) -> glutin::ControlFlow {
+ if let Some(ref mut events_loop) = self.0 {
+ events_loop.run_forever(callback);
+ } else {
+ loop {
+ self.sleep();
+ if callback(glutin::Event::Awakened) == glutin::ControlFlow::Break {
+ break;
+ }
+ }
+ }
+ }
+ fn sleep(&self) {
+ thread::sleep(time::Duration::from_millis(5));
+ }
+}
+
+struct HeadedEventLoopWaker {
+ proxy: Arc<glutin::EventsLoopProxy>,
+}
+impl HeadedEventLoopWaker {
+ fn new(events_loop: &glutin::EventsLoop) -> HeadedEventLoopWaker {
+ let proxy = Arc::new(events_loop.create_proxy());
+ HeadedEventLoopWaker { proxy }
+ }
+}
+impl EventLoopWaker for HeadedEventLoopWaker {
+ fn wake(&self) {
+ // kick the OS event loop awake.
+ if let Err(err) = self.proxy.wakeup() {
+ warn!("Failed to wake up event loop ({}).", err);
+ }
+ }
+ fn clone(&self) -> Box<dyn EventLoopWaker + Send> {
+ Box::new(HeadedEventLoopWaker {
+ proxy: self.proxy.clone(),
+ })
+ }
+}
+
+struct HeadlessEventLoopWaker;
+impl EventLoopWaker for HeadlessEventLoopWaker {
+ fn wake(&self) {}
+ fn clone(&self) -> Box<dyn EventLoopWaker + Send> { Box::new(HeadlessEventLoopWaker) }
+}
diff --git a/ports/glutin/headed_window.rs b/ports/glutin/headed_window.rs
new file mode 100644
index 00000000000..754cccf175b
--- /dev/null
+++ b/ports/glutin/headed_window.rs
@@ -0,0 +1,533 @@
+/* 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/. */
+
+//! A glutin window implementation.
+
+use crate::app;
+use crate::keyutils::keyboard_event_from_winit;
+use crate::window_trait::{WindowPortsMethods, LINE_HEIGHT};
+use euclid::{TypedPoint2D, TypedScale, TypedSize2D, TypedVector2D};
+use gleam::gl;
+use glutin::dpi::{LogicalPosition, LogicalSize, PhysicalSize};
+#[cfg(target_os = "macos")]
+use glutin::os::macos::{ActivationPolicy, WindowBuilderExt};
+#[cfg(any(target_os = "linux", target_os = "windows"))]
+use glutin::Icon;
+use glutin::{ContextBuilder, GlContext, GlWindow};
+use glutin::{ElementState, KeyboardInput, MouseButton, MouseScrollDelta, TouchPhase};
+#[cfg(any(target_os = "linux", target_os = "windows"))]
+use image;
+use keyboard_types::{Key, KeyState, KeyboardEvent};
+use servo::compositing::windowing::{AnimationState, MouseWindowEvent, WindowEvent};
+use servo::compositing::windowing::{EmbedderCoordinates, WindowMethods};
+use servo::embedder_traits::Cursor;
+use servo::script_traits::TouchEventType;
+use servo::servo_config::opts;
+use servo::servo_geometry::DeviceIndependentPixel;
+use servo::style_traits::DevicePixel;
+use servo::webrender_api::{
+ DeviceIntPoint, DeviceIntRect, DeviceIntSize, FramebufferIntSize, ScrollLocation,
+};
+use std::cell::{Cell, RefCell};
+use std::mem;
+use std::rc::Rc;
+#[cfg(target_os = "windows")]
+use winapi;
+
+const MULTISAMPLES: u16 = 16;
+
+#[cfg(target_os = "macos")]
+fn builder_with_platform_options(mut builder: glutin::WindowBuilder) -> glutin::WindowBuilder {
+ if opts::get().output_file.is_some() {
+ // Prevent the window from showing in Dock.app, stealing focus,
+ // when generating an output file.
+ builder = builder.with_activation_policy(ActivationPolicy::Prohibited)
+ }
+ builder
+}
+
+#[cfg(not(target_os = "macos"))]
+fn builder_with_platform_options(builder: glutin::WindowBuilder) -> glutin::WindowBuilder {
+ builder
+}
+
+pub struct Window {
+ gl_window: GlWindow,
+ screen_size: TypedSize2D<u32, DeviceIndependentPixel>,
+ inner_size: Cell<TypedSize2D<u32, DeviceIndependentPixel>>,
+ mouse_down_button: Cell<Option<glutin::MouseButton>>,
+ mouse_down_point: Cell<TypedPoint2D<i32, DevicePixel>>,
+ primary_monitor: glutin::MonitorId,
+ event_queue: RefCell<Vec<WindowEvent>>,
+ mouse_pos: Cell<TypedPoint2D<i32, DevicePixel>>,
+ last_pressed: Cell<Option<KeyboardEvent>>,
+ animation_state: Cell<AnimationState>,
+ fullscreen: Cell<bool>,
+ gl: Rc<dyn gl::Gl>,
+}
+
+#[cfg(not(target_os = "windows"))]
+fn window_creation_scale_factor() -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> {
+ TypedScale::new(1.0)
+}
+
+#[cfg(target_os = "windows")]
+fn window_creation_scale_factor() -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> {
+ let hdc = unsafe { winapi::um::winuser::GetDC(::std::ptr::null_mut()) };
+ let ppi = unsafe { winapi::um::wingdi::GetDeviceCaps(hdc, winapi::um::wingdi::LOGPIXELSY) };
+ TypedScale::new(ppi as f32 / 96.0)
+}
+
+impl Window {
+ pub fn new(
+ win_size: TypedSize2D<u32, DeviceIndependentPixel>,
+ events_loop: &glutin::EventsLoop,
+ ) -> Rc<dyn WindowPortsMethods> {
+ let opts = opts::get();
+
+ // If there's no chrome, start off with the window invisible. It will be set to visible in
+ // `load_end()`. This avoids an ugly flash of unstyled content (especially important since
+ // unstyled content is white and chrome often has a transparent background). See issue
+ // #9996.
+ let visible = opts.output_file.is_none() && !opts.no_native_titlebar;
+
+ let win_size: DeviceIntSize = (win_size.to_f32() * window_creation_scale_factor()).to_i32();
+ let width = win_size.to_untyped().width;
+ let height = win_size.to_untyped().height;
+
+ let mut window_builder = glutin::WindowBuilder::new()
+ .with_title("Servo".to_string())
+ .with_decorations(!opts.no_native_titlebar)
+ .with_transparency(opts.no_native_titlebar)
+ .with_dimensions(LogicalSize::new(width as f64, height as f64))
+ .with_visibility(visible)
+ .with_multitouch();
+
+ window_builder = builder_with_platform_options(window_builder);
+
+ let mut context_builder = ContextBuilder::new()
+ .with_gl(app::gl_version())
+ .with_vsync(opts.enable_vsync);
+
+ if opts.use_msaa {
+ context_builder = context_builder.with_multisampling(MULTISAMPLES)
+ }
+
+ let glutin_window = GlWindow::new(window_builder, context_builder, &events_loop)
+ .expect("Failed to create window.");
+
+ #[cfg(any(target_os = "linux", target_os = "windows"))]
+ {
+ let icon_bytes = include_bytes!("../../resources/servo64.png");
+ glutin_window.set_window_icon(Some(load_icon(icon_bytes)));
+ }
+
+ unsafe {
+ glutin_window
+ .context()
+ .make_current()
+ .expect("Couldn't make window current");
+ }
+
+ let primary_monitor = events_loop.get_primary_monitor();
+
+ let PhysicalSize {
+ width: screen_width,
+ height: screen_height,
+ } = primary_monitor.get_dimensions();
+ let screen_size = TypedSize2D::new(screen_width as u32, screen_height as u32);
+ // TODO(ajeffrey): can this fail?
+ let LogicalSize { width, height } = glutin_window
+ .get_inner_size()
+ .expect("Failed to get window inner size.");
+ let inner_size = TypedSize2D::new(width as u32, height as u32);
+
+ glutin_window.show();
+
+ let gl = match gl::GlType::default() {
+ gl::GlType::Gl => unsafe {
+ gl::GlFns::load_with(|s| glutin_window.get_proc_address(s) as *const _)
+ },
+ gl::GlType::Gles => unsafe {
+ gl::GlesFns::load_with(|s| glutin_window.get_proc_address(s) as *const _)
+ },
+ };
+
+ gl.clear_color(0.6, 0.6, 0.6, 1.0);
+ gl.clear(gl::COLOR_BUFFER_BIT);
+ gl.finish();
+
+ let window = Window {
+ gl_window: glutin_window,
+ event_queue: RefCell::new(vec![]),
+ mouse_down_button: Cell::new(None),
+ mouse_down_point: Cell::new(TypedPoint2D::new(0, 0)),
+ mouse_pos: Cell::new(TypedPoint2D::new(0, 0)),
+ last_pressed: Cell::new(None),
+ gl: gl.clone(),
+ animation_state: Cell::new(AnimationState::Idle),
+ fullscreen: Cell::new(false),
+ inner_size: Cell::new(inner_size),
+ primary_monitor,
+ screen_size,
+ };
+
+ window.present();
+
+ Rc::new(window)
+ }
+
+ fn handle_received_character(&self, mut ch: char) {
+ info!("winit received character: {:?}", ch);
+ if ch.is_control() {
+ if ch as u8 >= 32 {
+ return;
+ }
+ // shift ASCII control characters to lowercase
+ ch = (ch as u8 + 96) as char;
+ }
+ let mut event = if let Some(event) = self.last_pressed.replace(None) {
+ event
+ } else if ch.is_ascii() {
+ // Some keys like Backspace emit a control character in winit
+ // but they are already dealt with in handle_keyboard_input
+ // so just ignore the character.
+ return;
+ } else {
+ // For combined characters like the letter e with an acute accent
+ // no keyboard event is emitted. A dummy event is created in this case.
+ KeyboardEvent::default()
+ };
+ event.key = Key::Character(ch.to_string());
+ self.event_queue
+ .borrow_mut()
+ .push(WindowEvent::Keyboard(event));
+ }
+
+ fn handle_keyboard_input(&self, input: KeyboardInput) {
+ let event = keyboard_event_from_winit(input);
+ if event.state == KeyState::Down && event.key == Key::Unidentified {
+ // If pressed and probably printable, we expect a ReceivedCharacter event.
+ self.last_pressed.set(Some(event));
+ } else if event.key != Key::Unidentified {
+ self.last_pressed.set(None);
+ self.event_queue
+ .borrow_mut()
+ .push(WindowEvent::Keyboard(event));
+ }
+ }
+
+ /// Helper function to handle a click
+ fn handle_mouse(
+ &self,
+ button: glutin::MouseButton,
+ action: glutin::ElementState,
+ coords: TypedPoint2D<i32, DevicePixel>,
+ ) {
+ use servo::script_traits::MouseButton;
+
+ let max_pixel_dist = 10.0 * self.servo_hidpi_factor().get();
+ let event = match action {
+ ElementState::Pressed => {
+ self.mouse_down_point.set(coords);
+ self.mouse_down_button.set(Some(button));
+ MouseWindowEvent::MouseDown(MouseButton::Left, coords.to_f32())
+ },
+ ElementState::Released => {
+ let mouse_up_event = MouseWindowEvent::MouseUp(MouseButton::Left, coords.to_f32());
+ match self.mouse_down_button.get() {
+ None => mouse_up_event,
+ Some(but) if button == but => {
+ let pixel_dist = self.mouse_down_point.get() - coords;
+ let pixel_dist =
+ ((pixel_dist.x * pixel_dist.x + pixel_dist.y * pixel_dist.y) as f32)
+ .sqrt();
+ if pixel_dist < max_pixel_dist {
+ self.event_queue
+ .borrow_mut()
+ .push(WindowEvent::MouseWindowEventClass(mouse_up_event));
+ MouseWindowEvent::Click(MouseButton::Left, coords.to_f32())
+ } else {
+ mouse_up_event
+ }
+ },
+ Some(_) => mouse_up_event,
+ }
+ },
+ };
+ self.event_queue
+ .borrow_mut()
+ .push(WindowEvent::MouseWindowEventClass(event));
+ }
+
+ fn device_hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> {
+ TypedScale::new(self.gl_window.get_hidpi_factor() as f32)
+ }
+
+ fn servo_hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> {
+ match opts::get().device_pixels_per_px {
+ Some(device_pixels_per_px) => TypedScale::new(device_pixels_per_px),
+ _ => match opts::get().output_file {
+ Some(_) => TypedScale::new(1.0),
+ None => self.device_hidpi_factor(),
+ },
+ }
+ }
+}
+
+impl WindowPortsMethods for Window {
+ 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()
+ }
+
+ fn page_height(&self) -> f32 {
+ let dpr = self.servo_hidpi_factor();
+ let size = self
+ .gl_window
+ .get_inner_size()
+ .expect("Failed to get window inner size.");
+ size.height as f32 * dpr.get()
+ }
+
+ fn set_title(&self, title: &str) {
+ self.gl_window.set_title(title);
+ }
+
+ fn set_inner_size(&self, size: DeviceIntSize) {
+ let size = size.to_f32() / self.device_hidpi_factor();
+ self.gl_window
+ .set_inner_size(LogicalSize::new(size.width.into(), size.height.into()))
+ }
+
+ fn set_position(&self, point: DeviceIntPoint) {
+ let point = point.to_f32() / self.device_hidpi_factor();
+ self.gl_window
+ .set_position(LogicalPosition::new(point.x.into(), point.y.into()))
+ }
+
+ fn set_fullscreen(&self, state: bool) {
+ if self.fullscreen.get() != state {
+ self.gl_window
+ .set_fullscreen(Some(self.primary_monitor.clone()));
+ }
+ self.fullscreen.set(state);
+ }
+
+ fn get_fullscreen(&self) -> bool {
+ return self.fullscreen.get();
+ }
+
+ fn set_cursor(&self, cursor: Cursor) {
+ use glutin::MouseCursor;
+
+ let winit_cursor = match cursor {
+ Cursor::Default => MouseCursor::Default,
+ Cursor::Pointer => MouseCursor::Hand,
+ Cursor::ContextMenu => MouseCursor::ContextMenu,
+ Cursor::Help => MouseCursor::Help,
+ Cursor::Progress => MouseCursor::Progress,
+ Cursor::Wait => MouseCursor::Wait,
+ Cursor::Cell => MouseCursor::Cell,
+ Cursor::Crosshair => MouseCursor::Crosshair,
+ Cursor::Text => MouseCursor::Text,
+ Cursor::VerticalText => MouseCursor::VerticalText,
+ Cursor::Alias => MouseCursor::Alias,
+ Cursor::Copy => MouseCursor::Copy,
+ Cursor::Move => MouseCursor::Move,
+ Cursor::NoDrop => MouseCursor::NoDrop,
+ Cursor::NotAllowed => MouseCursor::NotAllowed,
+ Cursor::Grab => MouseCursor::Grab,
+ Cursor::Grabbing => MouseCursor::Grabbing,
+ Cursor::EResize => MouseCursor::EResize,
+ Cursor::NResize => MouseCursor::NResize,
+ Cursor::NeResize => MouseCursor::NeResize,
+ Cursor::NwResize => MouseCursor::NwResize,
+ Cursor::SResize => MouseCursor::SResize,
+ Cursor::SeResize => MouseCursor::SeResize,
+ Cursor::SwResize => MouseCursor::SwResize,
+ Cursor::WResize => MouseCursor::WResize,
+ Cursor::EwResize => MouseCursor::EwResize,
+ Cursor::NsResize => MouseCursor::NsResize,
+ Cursor::NeswResize => MouseCursor::NeswResize,
+ Cursor::NwseResize => MouseCursor::NwseResize,
+ Cursor::ColResize => MouseCursor::ColResize,
+ Cursor::RowResize => MouseCursor::RowResize,
+ Cursor::AllScroll => MouseCursor::AllScroll,
+ Cursor::ZoomIn => MouseCursor::ZoomIn,
+ Cursor::ZoomOut => MouseCursor::ZoomOut,
+ _ => MouseCursor::Default,
+ };
+ self.gl_window.set_cursor(winit_cursor);
+ }
+
+ fn is_animating(&self) -> bool {
+ self.animation_state.get() == AnimationState::Animating
+ }
+
+ fn id(&self) -> Option<glutin::WindowId> {
+ Some(self.gl_window.id())
+ }
+
+ fn winit_event_to_servo_event(&self, event: glutin::WindowEvent) {
+ match event {
+ glutin::WindowEvent::ReceivedCharacter(ch) => self.handle_received_character(ch),
+ glutin::WindowEvent::KeyboardInput { input, .. } => self.handle_keyboard_input(input),
+ glutin::WindowEvent::MouseInput { state, button, .. } => {
+ if button == MouseButton::Left || button == MouseButton::Right {
+ self.handle_mouse(button, state, self.mouse_pos.get());
+ }
+ },
+ glutin::WindowEvent::CursorMoved { position, .. } => {
+ let pos = position.to_physical(self.device_hidpi_factor().get() as f64);
+ let (x, y): (i32, i32) = pos.into();
+ self.mouse_pos.set(TypedPoint2D::new(x, y));
+ self.event_queue
+ .borrow_mut()
+ .push(WindowEvent::MouseWindowMoveEventClass(TypedPoint2D::new(
+ x as f32, y as f32,
+ )));
+ },
+ glutin::WindowEvent::MouseWheel { delta, phase, .. } => {
+ let (mut dx, mut dy) = match delta {
+ MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
+ MouseScrollDelta::PixelDelta(position) => {
+ let position =
+ position.to_physical(self.device_hidpi_factor().get() as f64);
+ (position.x as f32, position.y as f32)
+ },
+ };
+ // Scroll events snap to the major axis of movement, with vertical
+ // preferred over horizontal.
+ if dy.abs() >= dx.abs() {
+ dx = 0.0;
+ } else {
+ dy = 0.0;
+ }
+
+ let scroll_location = ScrollLocation::Delta(TypedVector2D::new(dx, dy));
+ let phase = winit_phase_to_touch_event_type(phase);
+ let event = WindowEvent::Scroll(scroll_location, self.mouse_pos.get(), phase);
+ self.event_queue.borrow_mut().push(event);
+ },
+ glutin::WindowEvent::Touch(touch) => {
+ use servo::script_traits::TouchId;
+
+ let phase = winit_phase_to_touch_event_type(touch.phase);
+ let id = TouchId(touch.id as i32);
+ let position = touch
+ .location
+ .to_physical(self.device_hidpi_factor().get() as f64);
+ let point = TypedPoint2D::new(position.x as f32, position.y as f32);
+ self.event_queue
+ .borrow_mut()
+ .push(WindowEvent::Touch(phase, id, point));
+ },
+ glutin::WindowEvent::Refresh => {
+ self.event_queue.borrow_mut().push(WindowEvent::Refresh);
+ },
+ glutin::WindowEvent::CloseRequested => {
+ self.event_queue.borrow_mut().push(WindowEvent::Quit);
+ },
+ glutin::WindowEvent::Resized(size) => {
+ // size is DeviceIndependentPixel.
+ // gl_window.resize() takes DevicePixel.
+ let size = size.to_physical(self.device_hidpi_factor().get() as f64);
+ self.gl_window.resize(size);
+ // window.set_inner_size() takes DeviceIndependentPixel.
+ let (width, height) = size.into();
+ let new_size = TypedSize2D::new(width, height);
+ if self.inner_size.get() != new_size {
+ self.inner_size.set(new_size);
+ self.event_queue.borrow_mut().push(WindowEvent::Resize);
+ }
+ },
+ _ => {},
+ }
+ }
+}
+
+impl WindowMethods for Window {
+ fn gl(&self) -> Rc<dyn gl::Gl> {
+ self.gl.clone()
+ }
+
+ fn get_coordinates(&self) -> EmbedderCoordinates {
+ // TODO(ajeffrey): can this fail?
+ let dpr = self.device_hidpi_factor();
+ let LogicalSize { width, height } = self
+ .gl_window
+ .get_outer_size()
+ .expect("Failed to get window outer size.");
+ let LogicalPosition { x, y } = self
+ .gl_window
+ .get_position()
+ .unwrap_or(LogicalPosition::new(0., 0.));
+ let win_size = (TypedSize2D::new(width as f32, height as f32) * dpr).to_i32();
+ let win_origin = (TypedPoint2D::new(x as f32, y as f32) * dpr).to_i32();
+ let screen = (self.screen_size.to_f32() * dpr).to_i32();
+
+ let LogicalSize { width, height } = self
+ .gl_window
+ .get_inner_size()
+ .expect("Failed to get window inner size.");
+ let inner_size = (TypedSize2D::new(width as f32, height as f32) * dpr).to_i32();
+ let viewport = DeviceIntRect::new(TypedPoint2D::zero(), inner_size);
+ let framebuffer = FramebufferIntSize::from_untyped(&viewport.size.to_untyped());
+
+ EmbedderCoordinates {
+ viewport,
+ framebuffer,
+ window: (win_size, win_origin),
+ screen: screen,
+ // FIXME: Glutin doesn't have API for available size. Fallback to screen size
+ screen_avail: screen,
+ hidpi_factor: self.servo_hidpi_factor(),
+ }
+ }
+
+ fn present(&self) {
+ if let Err(err) = self.gl_window.swap_buffers() {
+ warn!("Failed to swap window buffers ({}).", err);
+ }
+ }
+
+ fn set_animation_state(&self, state: AnimationState) {
+ self.animation_state.set(state);
+ }
+
+ fn prepare_for_composite(&self) -> bool {
+ if let Err(err) = unsafe { self.gl_window.context().make_current() } {
+ warn!("Couldn't make window current: {}", err);
+ }
+ true
+ }
+}
+
+fn winit_phase_to_touch_event_type(phase: TouchPhase) -> TouchEventType {
+ match phase {
+ TouchPhase::Started => TouchEventType::Down,
+ TouchPhase::Moved => TouchEventType::Move,
+ TouchPhase::Ended => TouchEventType::Up,
+ TouchPhase::Cancelled => TouchEventType::Cancel,
+ }
+}
+
+#[cfg(any(target_os = "linux", target_os = "windows"))]
+fn load_icon(icon_bytes: &[u8]) -> Icon {
+ let (icon_rgba, icon_width, icon_height) = {
+ use image::{GenericImageView, Pixel};
+ let image = image::load_from_memory(icon_bytes).expect("Failed to load icon");;
+ let (width, height) = image.dimensions();
+ let mut rgba = Vec::with_capacity((width * height) as usize * 4);
+ for (_, _, pixel) in image.pixels() {
+ rgba.extend_from_slice(&pixel.to_rgba().data);
+ }
+ (rgba, width, height)
+ };
+ Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to load icon")
+}
diff --git a/ports/glutin/headless_window.rs b/ports/glutin/headless_window.rs
new file mode 100644
index 00000000000..c14228048b7
--- /dev/null
+++ b/ports/glutin/headless_window.rs
@@ -0,0 +1,200 @@
+/* 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/. */
+
+//! A headless window implementation.
+
+use crate::window_trait::WindowPortsMethods;
+use glutin;
+use euclid::{TypedPoint2D, TypedScale, TypedSize2D};
+use gleam::gl;
+use servo::compositing::windowing::{AnimationState, WindowEvent};
+use servo::compositing::windowing::{EmbedderCoordinates, WindowMethods};
+use servo::servo_config::opts;
+use servo::servo_geometry::DeviceIndependentPixel;
+use servo::style_traits::DevicePixel;
+use servo::webrender_api::{DeviceIntRect, FramebufferIntSize};
+use std::cell::Cell;
+#[cfg(any(target_os = "linux", target_os = "macos"))]
+use std::ffi::CString;
+#[cfg(any(target_os = "linux", target_os = "macos"))]
+use std::mem;
+use std::os::raw::c_void;
+use std::ptr;
+use std::rc::Rc;
+
+#[cfg(any(target_os = "linux", target_os = "macos"))]
+struct HeadlessContext {
+ width: u32,
+ height: u32,
+ _context: osmesa_sys::OSMesaContext,
+ _buffer: Vec<u32>,
+}
+
+#[cfg(not(any(target_os = "linux", target_os = "macos")))]
+struct HeadlessContext {
+ width: u32,
+ height: u32,
+}
+
+impl HeadlessContext {
+ #[cfg(any(target_os = "linux", target_os = "macos"))]
+ fn new(width: u32, height: u32) -> HeadlessContext {
+ let mut attribs = Vec::new();
+
+ attribs.push(osmesa_sys::OSMESA_PROFILE);
+ attribs.push(osmesa_sys::OSMESA_CORE_PROFILE);
+ attribs.push(osmesa_sys::OSMESA_CONTEXT_MAJOR_VERSION);
+ attribs.push(3);
+ attribs.push(osmesa_sys::OSMESA_CONTEXT_MINOR_VERSION);
+ attribs.push(3);
+ attribs.push(0);
+
+ let context =
+ unsafe { osmesa_sys::OSMesaCreateContextAttribs(attribs.as_ptr(), ptr::null_mut()) };
+
+ assert!(!context.is_null());
+
+ let mut buffer = vec![0; (width * height) as usize];
+
+ unsafe {
+ let ret = osmesa_sys::OSMesaMakeCurrent(
+ context,
+ buffer.as_mut_ptr() as *mut _,
+ gl::UNSIGNED_BYTE,
+ width as i32,
+ height as i32,
+ );
+ assert_ne!(ret, 0);
+ };
+
+ HeadlessContext {
+ width: width,
+ height: height,
+ _context: context,
+ _buffer: buffer,
+ }
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "macos")))]
+ fn new(width: u32, height: u32) -> HeadlessContext {
+ HeadlessContext {
+ width: width,
+ height: height,
+ }
+ }
+
+ #[cfg(any(target_os = "linux", target_os = "macos"))]
+ fn get_proc_address(s: &str) -> *const c_void {
+ let c_str = CString::new(s).expect("Unable to create CString");
+ unsafe { mem::transmute(osmesa_sys::OSMesaGetProcAddress(c_str.as_ptr())) }
+ }
+
+ #[cfg(not(any(target_os = "linux", target_os = "macos")))]
+ fn get_proc_address(_: &str) -> *const c_void {
+ ptr::null() as *const _
+ }
+}
+
+pub struct Window {
+ context: HeadlessContext,
+ animation_state: Cell<AnimationState>,
+ fullscreen: Cell<bool>,
+ gl: Rc<dyn gl::Gl>,
+}
+
+impl Window {
+ pub fn new(size: TypedSize2D<u32, DeviceIndependentPixel>) -> Rc<dyn WindowPortsMethods> {
+ let context = HeadlessContext::new(size.width, size.height);
+ let gl = unsafe { gl::GlFns::load_with(|s| HeadlessContext::get_proc_address(s)) };
+
+ // Print some information about the headless renderer that
+ // can be useful in diagnosing CI failures on build machines.
+ println!("{}", gl.get_string(gl::VENDOR));
+ println!("{}", gl.get_string(gl::RENDERER));
+ println!("{}", gl.get_string(gl::VERSION));
+
+ let window = Window {
+ context,
+ gl,
+ animation_state: Cell::new(AnimationState::Idle),
+ fullscreen: Cell::new(false),
+ };
+
+ Rc::new(window)
+ }
+
+ fn servo_hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> {
+ match opts::get().device_pixels_per_px {
+ Some(device_pixels_per_px) => TypedScale::new(device_pixels_per_px),
+ _ => TypedScale::new(1.0),
+ }
+ }
+}
+
+impl WindowPortsMethods for Window {
+ fn get_events(&self) -> Vec<WindowEvent> {
+ vec![]
+ }
+
+ fn has_events(&self) -> bool {
+ false
+ }
+
+ fn id(&self) -> Option<glutin::WindowId> {
+ None
+ }
+
+ fn page_height(&self) -> f32 {
+ let dpr = self.servo_hidpi_factor();
+ self.context.height as f32 * dpr.get()
+ }
+
+ fn set_fullscreen(&self, state: bool) {
+ self.fullscreen.set(state);
+ }
+
+ fn get_fullscreen(&self) -> bool {
+ return self.fullscreen.get();
+ }
+
+ fn is_animating(&self) -> bool {
+ self.animation_state.get() == AnimationState::Animating
+ }
+
+ fn winit_event_to_servo_event(&self, _event: glutin::WindowEvent) {
+ // Not expecting any winit events.
+ }
+}
+
+impl WindowMethods for Window {
+ fn gl(&self) -> Rc<dyn gl::Gl> {
+ self.gl.clone()
+ }
+
+ fn get_coordinates(&self) -> EmbedderCoordinates {
+ let dpr = self.servo_hidpi_factor();
+ let size =
+ (TypedSize2D::new(self.context.width, self.context.height).to_f32() * dpr).to_i32();
+ let viewport = DeviceIntRect::new(TypedPoint2D::zero(), size);
+ let framebuffer = FramebufferIntSize::from_untyped(&size.to_untyped());
+ EmbedderCoordinates {
+ viewport,
+ framebuffer,
+ window: (size, TypedPoint2D::zero()),
+ screen: size,
+ screen_avail: size,
+ hidpi_factor: dpr,
+ }
+ }
+
+ fn present(&self) {}
+
+ fn set_animation_state(&self, state: AnimationState) {
+ self.animation_state.set(state);
+ }
+
+ fn prepare_for_composite(&self) -> bool {
+ true
+ }
+}
diff --git a/ports/servo/glutin_app/keyutils.rs b/ports/glutin/keyutils.rs
index add042a2382..7dd7fdf5ca5 100644
--- a/ports/servo/glutin_app/keyutils.rs
+++ b/ports/glutin/keyutils.rs
@@ -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 https://mozilla.org/MPL/2.0/. */
-use keyboard_types::{Code, Key, KeyboardEvent, KeyState, Modifiers, Location};
use glutin::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode};
+use keyboard_types::{Code, Key, KeyState, KeyboardEvent, Location, Modifiers};
// Some shortcuts use Cmd on Mac and Control on other systems.
#[cfg(target_os = "macos")]
diff --git a/ports/servo/main.rs b/ports/glutin/main.rs
index 17e270a0115..23be648bad8 100644
--- a/ports/servo/main.rs
+++ b/ports/glutin/main.rs
@@ -17,14 +17,8 @@
#![cfg_attr(feature = "unstable", feature(core_intrinsics))]
-// Have this here rather than in non_android_main.rs to work around
-// https://github.com/rust-lang/rust/issues/53205
#[cfg(not(target_os = "android"))]
-#[macro_use]
-extern crate log;
-
-#[cfg(not(target_os = "android"))]
-include!("non_android_main.rs");
+include!("main2.rs");
#[cfg(target_os = "android")]
pub fn main() {
diff --git a/ports/servo/non_android_main.rs b/ports/glutin/main2.rs
index 767d27a646c..73fa1caf618 100644
--- a/ports/servo/non_android_main.rs
+++ b/ports/glutin/main2.rs
@@ -2,24 +2,29 @@
* 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/. */
-#[macro_use] extern crate lazy_static;
+#[macro_use]
+extern crate lazy_static;
+#[macro_use]
+extern crate log;
#[cfg(all(feature = "unstable", any(target_os = "macos", target_os = "linux")))]
-#[macro_use] extern crate sig;
-
-// The window backed by glutin
-mod glutin_app;
-
-mod resources;
+#[macro_use]
+extern crate sig;
+mod app;
mod browser;
+mod embedder;
+mod events_loop;
+mod headed_window;
+mod headless_window;
+mod keyutils;
+mod resources;
+mod skia_symbols;
+mod window_trait;
+use app::App;
use backtrace::Backtrace;
-use servo::{Servo, BrowserId};
-use servo::compositing::windowing::WindowEvent;
-use servo::config::opts::{self, ArgumentParsingResult, parse_url_or_filename};
+use servo::config::opts::{self, ArgumentParsingResult};
use servo::config::servo_version;
-use servo::servo_config::pref;
-use servo::servo_url::ServoUrl;
use std::env;
use std::panic;
use std::process;
@@ -36,7 +41,10 @@ pub mod platform {
pub fn deinit() {}
}
-#[cfg(any(not(feature = "unstable"), not(any(target_os = "macos", target_os = "linux"))))]
+#[cfg(any(
+ not(feature = "unstable"),
+ not(any(target_os = "macos", target_os = "linux"))
+))]
fn install_crash_handler() {}
#[cfg(all(feature = "unstable", any(target_os = "macos", target_os = "linux")))]
@@ -122,112 +130,7 @@ pub fn main() {
process::exit(0);
}
- let window = glutin_app::create_window();
-
- let mut browser = browser::Browser::new(window.clone());
-
- // 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();
-
- let target_url = cmdline_url.or(pref_url).or(blank_url).unwrap();
-
- let mut servo = Servo::new(window.clone());
- let browser_id = BrowserId::new();
- servo.handle_events(vec![WindowEvent::NewBrowser(target_url, browser_id)]);
-
- servo.setup_logging();
-
- window.run(|| {
- let win_events = 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,
- });
-
- browser.handle_window_events(win_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
- });
-
- servo.deinit();
+ App::run();
platform::deinit()
}
-
-// These functions aren't actually called. They are here as a link
-// hack because Skia references them.
-
-#[allow(non_snake_case)]
-#[no_mangle]
-pub extern "C" fn glBindVertexArrayOES(_array: usize) {
- unimplemented!()
-}
-
-#[allow(non_snake_case)]
-#[no_mangle]
-pub extern "C" fn glDeleteVertexArraysOES(_n: isize, _arrays: *const ()) {
- unimplemented!()
-}
-
-#[allow(non_snake_case)]
-#[no_mangle]
-pub extern "C" fn glGenVertexArraysOES(_n: isize, _arrays: *const ()) {
- unimplemented!()
-}
-
-#[allow(non_snake_case)]
-#[no_mangle]
-pub extern "C" fn glRenderbufferStorageMultisampleIMG(
- _: isize,
- _: isize,
- _: isize,
- _: isize,
- _: isize,
-) {
- unimplemented!()
-}
-
-#[allow(non_snake_case)]
-#[no_mangle]
-pub extern "C" fn glFramebufferTexture2DMultisampleIMG(
- _: isize,
- _: isize,
- _: isize,
- _: isize,
- _: isize,
- _: isize,
-) {
- unimplemented!()
-}
-
-#[allow(non_snake_case)]
-#[no_mangle]
-pub extern "C" fn glDiscardFramebufferEXT(_: isize, _: isize, _: *const ()) {
- unimplemented!()
-}
diff --git a/ports/servo/platform/macos/Info.plist b/ports/glutin/platform/macos/Info.plist
index c2855eb82b4..c2855eb82b4 100644
--- a/ports/servo/platform/macos/Info.plist
+++ b/ports/glutin/platform/macos/Info.plist
diff --git a/ports/servo/platform/macos/count_threads.c b/ports/glutin/platform/macos/count_threads.c
index bc3328d278d..bc3328d278d 100644
--- a/ports/servo/platform/macos/count_threads.c
+++ b/ports/glutin/platform/macos/count_threads.c
diff --git a/ports/servo/platform/macos/mod.rs b/ports/glutin/platform/macos/mod.rs
index 808f07d8340..730f4072576 100644
--- a/ports/servo/platform/macos/mod.rs
+++ b/ports/glutin/platform/macos/mod.rs
@@ -14,18 +14,17 @@ pub fn deinit() {
ptr::read_volatile(&INFO_PLIST[0]);
}
- let thread_count = unsafe {
- macos_count_running_threads()
- };
+ let thread_count = unsafe { macos_count_running_threads() };
if thread_count != 1 {
- println!("{} threads are still running after shutdown (bad).", thread_count);
+ println!(
+ "{} threads are still running after shutdown (bad).",
+ thread_count
+ );
if opts::get().clean_shutdown {
println!("Waiting until all threads have shutdown");
loop {
- let thread_count = unsafe {
- macos_count_running_threads()
- };
+ let thread_count = unsafe { macos_count_running_threads() };
if thread_count == 1 {
break;
}
@@ -43,6 +42,6 @@ pub fn deinit() {
pub static INFO_PLIST: [u8; 619] = *include_bytes!("Info.plist");
#[link(name = "count_threads")]
-extern {
+extern "C" {
fn macos_count_running_threads() -> i32;
}
diff --git a/ports/servo/platform/windows/servo.exe.manifest b/ports/glutin/platform/windows/servo.exe.manifest
index 23b2abe1b72..23b2abe1b72 100644
--- a/ports/servo/platform/windows/servo.exe.manifest
+++ b/ports/glutin/platform/windows/servo.exe.manifest
diff --git a/ports/servo/resources.rs b/ports/glutin/resources.rs
index 5a48037f524..5a48037f524 100644
--- a/ports/servo/resources.rs
+++ b/ports/glutin/resources.rs
diff --git a/ports/glutin/skia_symbols.rs b/ports/glutin/skia_symbols.rs
new file mode 100644
index 00000000000..ceca7697dd3
--- /dev/null
+++ b/ports/glutin/skia_symbols.rs
@@ -0,0 +1,55 @@
+/* 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/. */
+
+//! These functions aren't actually called. They are here as a link
+//! hack because Skia references them.
+
+#[allow(non_snake_case)]
+#[no_mangle]
+pub extern "C" fn glBindVertexArrayOES(_array: usize) {
+ unimplemented!()
+}
+
+#[allow(non_snake_case)]
+#[no_mangle]
+pub extern "C" fn glDeleteVertexArraysOES(_n: isize, _arrays: *const ()) {
+ unimplemented!()
+}
+
+#[allow(non_snake_case)]
+#[no_mangle]
+pub extern "C" fn glGenVertexArraysOES(_n: isize, _arrays: *const ()) {
+ unimplemented!()
+}
+
+#[allow(non_snake_case)]
+#[no_mangle]
+pub extern "C" fn glRenderbufferStorageMultisampleIMG(
+ _: isize,
+ _: isize,
+ _: isize,
+ _: isize,
+ _: isize,
+) {
+ unimplemented!()
+}
+
+#[allow(non_snake_case)]
+#[no_mangle]
+pub extern "C" fn glFramebufferTexture2DMultisampleIMG(
+ _: isize,
+ _: isize,
+ _: isize,
+ _: isize,
+ _: isize,
+ _: isize,
+) {
+ unimplemented!()
+}
+
+#[allow(non_snake_case)]
+#[no_mangle]
+pub extern "C" fn glDiscardFramebufferEXT(_: isize, _: isize, _: *const ()) {
+ unimplemented!()
+}
diff --git a/ports/glutin/window_trait.rs b/ports/glutin/window_trait.rs
new file mode 100644
index 00000000000..83b208fe993
--- /dev/null
+++ b/ports/glutin/window_trait.rs
@@ -0,0 +1,29 @@
+/* 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/. */
+
+//! Definition of Window.
+//! Implemented by headless and headed windows.
+
+use glutin;
+use servo::compositing::windowing::{WindowEvent, WindowMethods};
+use servo::embedder_traits::Cursor;
+use servo::webrender_api::{DeviceIntPoint, DeviceIntSize};
+
+// This should vary by zoom level and maybe actual text size (focused or under cursor)
+pub const LINE_HEIGHT: f32 = 38.0;
+
+pub trait WindowPortsMethods: WindowMethods {
+ fn get_events(&self) -> Vec<WindowEvent>;
+ fn id(&self) -> Option<glutin::WindowId>;
+ fn has_events(&self) -> bool;
+ fn page_height(&self) -> f32;
+ fn get_fullscreen(&self) -> bool;
+ fn winit_event_to_servo_event(&self, event: glutin::WindowEvent);
+ fn is_animating(&self) -> bool;
+ fn set_title(&self, _title: &str) {}
+ fn set_inner_size(&self, _size: DeviceIntSize) {}
+ fn set_position(&self, _point: DeviceIntPoint) {}
+ fn set_fullscreen(&self, _state: bool) {}
+ fn set_cursor(&self, _cursor: Cursor) {}
+}
diff --git a/ports/libsimpleservo/api/src/lib.rs b/ports/libsimpleservo/api/src/lib.rs
index 09878dc2542..2df9da4fd76 100644
--- a/ports/libsimpleservo/api/src/lib.rs
+++ b/ports/libsimpleservo/api/src/lib.rs
@@ -10,7 +10,8 @@ pub mod gl_glue;
pub use servo::script_traits::MouseButton;
use servo::compositing::windowing::{
- AnimationState, EmbedderCoordinates, MouseWindowEvent, WindowEvent, WindowMethods,
+ AnimationState, EmbedderCoordinates, EmbedderMethods, MouseWindowEvent, WindowEvent,
+ WindowMethods,
};
use servo::embedder_traits::resources::{self, Resource, ResourceReaderMethods};
use servo::embedder_traits::EmbedderMsg;
@@ -112,9 +113,9 @@ pub trait HostTrait {
}
pub struct ServoGlue {
- servo: Servo<ServoCallbacks>,
+ servo: Servo<ServoWindowCallbacks>,
batch_mode: bool,
- callbacks: Rc<ServoCallbacks>,
+ callbacks: Rc<ServoWindowCallbacks>,
/// id of the top level browsing context. It is unique as tabs
/// are not supported yet. None until created.
browser_id: Option<BrowserId>,
@@ -169,22 +170,25 @@ pub fn init(
gl.clear(gl::COLOR_BUFFER_BIT);
gl.finish();
- let callbacks = Rc::new(ServoCallbacks {
+ let window_callbacks = Rc::new(ServoWindowCallbacks {
gl: gl.clone(),
host_callbacks: callbacks,
coordinates: RefCell::new(init_opts.coordinates),
density: init_opts.density,
+ });
+
+ let embedder_callbacks = Box::new(ServoEmbedderCallbacks {
vr_pointer: init_opts.vr_pointer,
waker,
});
- let servo = Servo::new(callbacks.clone());
+ let servo = Servo::new(embedder_callbacks, window_callbacks.clone());
SERVO.with(|s| {
let mut servo_glue = ServoGlue {
servo,
batch_mode: false,
- callbacks,
+ callbacks: window_callbacks,
browser_id: None,
browsers: vec![],
events: vec![],
@@ -546,16 +550,37 @@ impl ServoGlue {
}
}
-struct ServoCallbacks {
+struct ServoEmbedderCallbacks {
waker: Box<dyn EventLoopWaker>,
+ vr_pointer: Option<*mut c_void>,
+}
+
+struct ServoWindowCallbacks {
gl: Rc<dyn gl::Gl>,
host_callbacks: Box<dyn HostTrait>,
coordinates: RefCell<Coordinates>,
density: f32,
- vr_pointer: Option<*mut c_void>,
}
-impl WindowMethods for ServoCallbacks {
+impl EmbedderMethods for ServoEmbedderCallbacks {
+ fn register_vr_services(
+ &self,
+ services: &mut VRServiceManager,
+ _: &mut Vec<Box<VRMainThreadHeartbeat>>,
+ ) {
+ debug!("EmbedderMethods::register_vrexternal");
+ if let Some(ptr) = self.vr_pointer {
+ services.register_vrexternal(VRExternalShmemPtr::new(ptr));
+ }
+ }
+
+ fn create_event_loop_waker(&self) -> Box<dyn EventLoopWaker> {
+ debug!("EmbedderMethods::create_event_loop_waker");
+ self.waker.clone()
+ }
+}
+
+impl WindowMethods for ServoWindowCallbacks {
fn prepare_for_composite(&self) -> bool {
debug!("WindowMethods::prepare_for_composite");
self.host_callbacks.make_current();
@@ -567,11 +592,6 @@ impl WindowMethods for ServoCallbacks {
self.host_callbacks.flush();
}
- fn create_event_loop_waker(&self) -> Box<dyn EventLoopWaker> {
- debug!("WindowMethods::create_event_loop_waker");
- self.waker.clone()
- }
-
fn gl(&self) -> Rc<dyn gl::Gl> {
debug!("WindowMethods::gl");
self.gl.clone()
@@ -594,16 +614,6 @@ impl WindowMethods for ServoCallbacks {
hidpi_factor: TypedScale::new(self.density),
}
}
-
- fn register_vr_services(
- &self,
- services: &mut VRServiceManager,
- _: &mut Vec<Box<VRMainThreadHeartbeat>>,
- ) {
- if let Some(ptr) = self.vr_pointer {
- services.register_vrexternal(VRExternalShmemPtr::new(ptr));
- }
- }
}
struct ResourceReaderInstance;
diff --git a/ports/servo/glutin_app/mod.rs b/ports/servo/glutin_app/mod.rs
deleted file mode 100644
index 444a1b7796d..00000000000
--- a/ports/servo/glutin_app/mod.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-/* 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/. */
-
-//! A simple application that uses glutin to open a window for Servo to display in.
-
-pub mod keyutils;
-pub mod window;
-
-use servo::servo_config::opts;
-use std::rc::Rc;
-
-pub fn create_window() -> Rc<window::Window> {
- // Read command-line options.
- let opts = opts::get();
- let foreground = opts.output_file.is_none() && !opts.headless;
-
- // Open a window.
- window::Window::new(foreground, opts.initial_window_size)
-}
diff --git a/ports/servo/glutin_app/window.rs b/ports/servo/glutin_app/window.rs
deleted file mode 100644
index 8c4acf75f93..00000000000
--- a/ports/servo/glutin_app/window.rs
+++ /dev/null
@@ -1,854 +0,0 @@
-/* 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/. */
-
-//! A windowing implementation using winit.
-
-use euclid::{TypedPoint2D, TypedVector2D, TypedScale, TypedSize2D};
-use gleam::gl;
-use glutin::{Api, ContextBuilder, GlContext, GlRequest, GlWindow};
-#[cfg(any(target_os = "linux", target_os = "windows"))]
-use image;
-use keyboard_types::{Key, KeyboardEvent, KeyState};
-use rust_webvr::GlWindowVRService;
-use servo::compositing::windowing::{AnimationState, MouseWindowEvent, WindowEvent};
-use servo::compositing::windowing::{EmbedderCoordinates, WindowMethods};
-use servo::embedder_traits::{Cursor, EventLoopWaker};
-use servo::script_traits::TouchEventType;
-use servo::servo_config::{opts, pref};
-use servo::servo_geometry::DeviceIndependentPixel;
-use servo::style_traits::DevicePixel;
-use servo::webrender_api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, FramebufferIntSize, ScrollLocation};
-use servo::webvr::VRServiceManager;
-use servo::webvr_traits::WebVRMainThreadHeartbeat;
-use std::cell::{Cell, RefCell};
-#[cfg(any(target_os = "linux", target_os = "macos"))]
-use std::ffi::CString;
-use std::mem;
-use std::os::raw::c_void;
-use std::ptr;
-use std::rc::Rc;
-use std::sync::Arc;
-use std::thread;
-use std::time;
-use super::keyutils::keyboard_event_from_winit;
-#[cfg(target_os = "windows")]
-use winapi;
-use glutin::{ElementState, Event, MouseButton, MouseScrollDelta, TouchPhase, KeyboardInput};
-#[cfg(any(target_os = "linux", target_os = "windows"))]
-use glutin::Icon;
-use glutin::dpi::{LogicalPosition, LogicalSize, PhysicalSize};
-#[cfg(target_os = "macos")]
-use glutin::os::macos::{ActivationPolicy, WindowBuilderExt};
-
-// This should vary by zoom level and maybe actual text size (focused or under cursor)
-pub const LINE_HEIGHT: f32 = 38.0;
-
-const MULTISAMPLES: u16 = 16;
-
-#[cfg(target_os = "macos")]
-fn builder_with_platform_options(mut builder: glutin::WindowBuilder) -> glutin::WindowBuilder {
- if opts::get().headless || opts::get().output_file.is_some() {
- // Prevent the window from showing in Dock.app, stealing focus,
- // or appearing at all when running in headless mode or generating an
- // output file.
- builder = builder.with_activation_policy(ActivationPolicy::Prohibited)
- }
- builder
-}
-
-#[cfg(not(target_os = "macos"))]
-fn builder_with_platform_options(builder: glutin::WindowBuilder) -> glutin::WindowBuilder {
- builder
-}
-
-#[cfg(any(target_os = "linux", target_os = "macos"))]
-struct HeadlessContext {
- width: u32,
- height: u32,
- _context: osmesa_sys::OSMesaContext,
- _buffer: Vec<u32>,
-}
-
-#[cfg(not(any(target_os = "linux", target_os = "macos")))]
-struct HeadlessContext {
- width: u32,
- height: u32,
-}
-
-impl HeadlessContext {
- #[cfg(any(target_os = "linux", target_os = "macos"))]
- fn new(width: u32, height: u32) -> HeadlessContext {
- let mut attribs = Vec::new();
-
- attribs.push(osmesa_sys::OSMESA_PROFILE);
- attribs.push(osmesa_sys::OSMESA_CORE_PROFILE);
- attribs.push(osmesa_sys::OSMESA_CONTEXT_MAJOR_VERSION);
- attribs.push(3);
- attribs.push(osmesa_sys::OSMESA_CONTEXT_MINOR_VERSION);
- attribs.push(3);
- attribs.push(0);
-
- let context =
- unsafe { osmesa_sys::OSMesaCreateContextAttribs(attribs.as_ptr(), ptr::null_mut()) };
-
- assert!(!context.is_null());
-
- let mut buffer = vec![0; (width * height) as usize];
-
- unsafe {
- let ret = osmesa_sys::OSMesaMakeCurrent(
- context,
- buffer.as_mut_ptr() as *mut _,
- gl::UNSIGNED_BYTE,
- width as i32,
- height as i32,
- );
- assert_ne!(ret, 0);
- };
-
- HeadlessContext {
- width: width,
- height: height,
- _context: context,
- _buffer: buffer,
- }
- }
-
- #[cfg(not(any(target_os = "linux", target_os = "macos")))]
- fn new(width: u32, height: u32) -> HeadlessContext {
- HeadlessContext {
- width: width,
- height: height,
- }
- }
-
- #[cfg(any(target_os = "linux", target_os = "macos"))]
- fn get_proc_address(s: &str) -> *const c_void {
- let c_str = CString::new(s).expect("Unable to create CString");
- unsafe { mem::transmute(osmesa_sys::OSMesaGetProcAddress(c_str.as_ptr())) }
- }
-
- #[cfg(not(any(target_os = "linux", target_os = "macos")))]
- fn get_proc_address(_: &str) -> *const c_void {
- ptr::null() as *const _
- }
-}
-
-enum WindowKind {
- Window(GlWindow, RefCell<glutin::EventsLoop>),
- Headless(HeadlessContext),
-}
-
-/// The type of a window.
-pub struct Window {
- kind: WindowKind,
- screen_size: TypedSize2D<u32, DeviceIndependentPixel>,
- inner_size: Cell<TypedSize2D<u32, DeviceIndependentPixel>>,
- mouse_down_button: Cell<Option<glutin::MouseButton>>,
- mouse_down_point: Cell<TypedPoint2D<i32, DevicePixel>>,
- event_queue: RefCell<Vec<WindowEvent>>,
- mouse_pos: Cell<TypedPoint2D<i32, DevicePixel>>,
- last_pressed: Cell<Option<KeyboardEvent>>,
- animation_state: Cell<AnimationState>,
- fullscreen: Cell<bool>,
- gl: Rc<dyn gl::Gl>,
- suspended: Cell<bool>,
-}
-
-#[cfg(not(target_os = "windows"))]
-fn window_creation_scale_factor() -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> {
- TypedScale::new(1.0)
-}
-
-#[cfg(target_os = "windows")]
-fn window_creation_scale_factor() -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> {
- let hdc = unsafe { winapi::um::winuser::GetDC(::std::ptr::null_mut()) };
- let ppi = unsafe { winapi::um::wingdi::GetDeviceCaps(hdc, winapi::um::wingdi::LOGPIXELSY) };
- TypedScale::new(ppi as f32 / 96.0)
-}
-
-impl Window {
- pub fn new(
- is_foreground: bool,
- window_size: TypedSize2D<u32, DeviceIndependentPixel>,
- ) -> Rc<Window> {
- let win_size: DeviceIntSize =
- (window_size.to_f32() * window_creation_scale_factor()).to_i32();
- let width = win_size.to_untyped().width;
- let height = win_size.to_untyped().height;
-
- // If there's no chrome, start off with the window invisible. It will be set to visible in
- // `load_end()`. This avoids an ugly flash of unstyled content (especially important since
- // unstyled content is white and chrome often has a transparent background). See issue
- // #9996.
- let visible = is_foreground && !opts::get().no_native_titlebar;
-
- let screen_size;
- let inner_size;
- let window_kind = if opts::get().headless {
- screen_size = TypedSize2D::new(width as u32, height as u32);
- inner_size = TypedSize2D::new(width as u32, height as u32);
- WindowKind::Headless(HeadlessContext::new(width as u32, height as u32))
- } else {
- let events_loop = glutin::EventsLoop::new();
- let mut window_builder = glutin::WindowBuilder::new()
- .with_title("Servo".to_string())
- .with_decorations(!opts::get().no_native_titlebar)
- .with_transparency(opts::get().no_native_titlebar)
- .with_dimensions(LogicalSize::new(width as f64, height as f64))
- .with_visibility(visible)
- .with_multitouch();
-
- window_builder = builder_with_platform_options(window_builder);
-
- let mut context_builder = ContextBuilder::new()
- .with_gl(Window::gl_version())
- .with_vsync(opts::get().enable_vsync);
-
- if opts::get().use_msaa {
- context_builder = context_builder.with_multisampling(MULTISAMPLES)
- }
-
- let glutin_window = GlWindow::new(window_builder, context_builder, &events_loop)
- .expect("Failed to create window.");
-
- #[cfg(any(target_os = "linux", target_os = "windows"))]
- {
- let icon_bytes = include_bytes!("../../../resources/servo64.png");
- glutin_window.set_window_icon(Some(load_icon(icon_bytes)));
- }
-
- unsafe {
- glutin_window
- .context()
- .make_current()
- .expect("Couldn't make window current");
- }
-
- let PhysicalSize {
- width: screen_width,
- height: screen_height,
- } = events_loop.get_primary_monitor().get_dimensions();
- screen_size = TypedSize2D::new(screen_width as u32, screen_height as u32);
- // TODO(ajeffrey): can this fail?
- let LogicalSize { width, height } = glutin_window
- .get_inner_size()
- .expect("Failed to get window inner size.");
- inner_size = TypedSize2D::new(width as u32, height as u32);
-
- glutin_window.show();
-
- WindowKind::Window(glutin_window, RefCell::new(events_loop))
- };
-
- let gl = match window_kind {
- WindowKind::Window(ref window, ..) => match gl::GlType::default() {
- gl::GlType::Gl => unsafe {
- gl::GlFns::load_with(|s| window.get_proc_address(s) as *const _)
- },
- gl::GlType::Gles => unsafe {
- gl::GlesFns::load_with(|s| window.get_proc_address(s) as *const _)
- },
- },
- WindowKind::Headless(..) => unsafe {
- gl::GlFns::load_with(|s| HeadlessContext::get_proc_address(s))
- },
- };
-
- if opts::get().headless {
- // Print some information about the headless renderer that
- // can be useful in diagnosing CI failures on build machines.
- println!("{}", gl.get_string(gl::VENDOR));
- println!("{}", gl.get_string(gl::RENDERER));
- println!("{}", gl.get_string(gl::VERSION));
- }
-
- gl.clear_color(0.6, 0.6, 0.6, 1.0);
- gl.clear(gl::COLOR_BUFFER_BIT);
- gl.finish();
-
- let window = Window {
- kind: window_kind,
- event_queue: RefCell::new(vec![]),
- mouse_down_button: Cell::new(None),
- mouse_down_point: Cell::new(TypedPoint2D::new(0, 0)),
- mouse_pos: Cell::new(TypedPoint2D::new(0, 0)),
- last_pressed: Cell::new(None),
- gl: gl.clone(),
- animation_state: Cell::new(AnimationState::Idle),
- fullscreen: Cell::new(false),
- inner_size: Cell::new(inner_size),
- screen_size,
- suspended: Cell::new(false),
- };
-
- window.present();
-
- Rc::new(window)
- }
-
- pub fn get_events(&self) -> Vec<WindowEvent> {
- mem::replace(&mut *self.event_queue.borrow_mut(), Vec::new())
- }
-
- pub fn page_height(&self) -> f32 {
- let dpr = self.servo_hidpi_factor();
- match self.kind {
- WindowKind::Window(ref window, _) => {
- let size = window
- .get_inner_size()
- .expect("Failed to get window inner size.");
- size.height as f32 * dpr.get()
- },
- WindowKind::Headless(ref context) => context.height as f32 * dpr.get(),
- }
- }
-
- pub fn set_title(&self, title: &str) {
- if let WindowKind::Window(ref window, _) = self.kind {
- window.set_title(title);
- }
- }
-
- pub fn set_inner_size(&self, size: DeviceIntSize) {
- if let WindowKind::Window(ref window, _) = self.kind {
- let size = size.to_f32() / self.device_hidpi_factor();
- window.set_inner_size(LogicalSize::new(size.width.into(), size.height.into()))
- }
- }
-
- pub fn set_position(&self, point: DeviceIntPoint) {
- if let WindowKind::Window(ref window, _) = self.kind {
- let point = point.to_f32() / self.device_hidpi_factor();
- window.set_position(LogicalPosition::new(point.x.into(), point.y.into()))
- }
- }
-
- pub fn set_fullscreen(&self, state: bool) {
- match self.kind {
- WindowKind::Window(ref window, ..) => {
- if self.fullscreen.get() != state {
- window.set_fullscreen(Some(window.get_primary_monitor()));
- }
- },
- WindowKind::Headless(..) => {},
- }
- self.fullscreen.set(state);
- }
-
- pub fn get_fullscreen(&self) -> bool {
- return self.fullscreen.get();
- }
-
- fn is_animating(&self) -> bool {
- self.animation_state.get() == AnimationState::Animating && !self.suspended.get()
- }
-
- pub fn run<T>(&self, mut servo_callback: T)
- where
- T: FnMut() -> bool,
- {
- match self.kind {
- WindowKind::Window(_, ref events_loop) => {
- let mut stop = false;
- loop {
- if self.is_animating() {
- // We block on compositing (servo_callback ends up calling swap_buffers)
- events_loop.borrow_mut().poll_events(|e| {
- self.winit_event_to_servo_event(e);
- });
- stop = servo_callback();
- } else {
- // We block on winit's event loop (window events)
- events_loop.borrow_mut().run_forever(|e| {
- self.winit_event_to_servo_event(e);
- if !self.event_queue.borrow().is_empty() {
- if !self.suspended.get() {
- stop = servo_callback();
- }
- }
- if stop || self.is_animating() {
- glutin::ControlFlow::Break
- } else {
- glutin::ControlFlow::Continue
- }
- });
- }
- if stop {
- break;
- }
- }
- },
- WindowKind::Headless(..) => {
- loop {
- // Sleep the main thread to avoid using 100% CPU
- // This can be done better, see comments in #18777
- if self.event_queue.borrow().is_empty() {
- thread::sleep(time::Duration::from_millis(5));
- }
- let stop = servo_callback();
- if stop {
- break;
- }
- }
- },
- }
- }
-
- #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
- fn gl_version() -> GlRequest {
- return GlRequest::Specific(Api::OpenGl, (3, 2));
- }
-
- #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
- fn gl_version() -> GlRequest {
- GlRequest::Specific(Api::OpenGlEs, (3, 0))
- }
-
- fn handle_received_character(&self, mut ch: char) {
- info!("winit received character: {:?}", ch);
- if ch.is_control() {
- if ch as u8 >= 32 {
- return;
- }
- // shift ASCII control characters to lowercase
- ch = (ch as u8 + 96) as char;
- }
- let mut event = if let Some(event) = self.last_pressed.replace(None) {
- event
- } else if ch.is_ascii() {
- // Some keys like Backspace emit a control character in winit
- // but they are already dealt with in handle_keyboard_input
- // so just ignore the character.
- return
- } else {
- // For combined characters like the letter e with an acute accent
- // no keyboard event is emitted. A dummy event is created in this case.
- KeyboardEvent::default()
- };
- event.key = Key::Character(ch.to_string());
- self.event_queue
- .borrow_mut()
- .push(WindowEvent::Keyboard(event));
- }
-
- fn handle_keyboard_input(
- &self,
- input: KeyboardInput,
- ) {
- let event = keyboard_event_from_winit(input);
- if event.state == KeyState::Down && event.key == Key::Unidentified {
- // If pressed and probably printable, we expect a ReceivedCharacter event.
- self.last_pressed.set(Some(event));
- } else if event.key != Key::Unidentified {
- self.last_pressed.set(None);
- self.event_queue
- .borrow_mut()
- .push(WindowEvent::Keyboard(event));
- }
- }
-
- fn winit_event_to_servo_event(&self, event: glutin::Event) {
- if let WindowKind::Window(ref window, _) = self.kind {
- if let Event::WindowEvent { window_id, .. } = event {
- if window.id() != window_id {
- return;
- }
- }
- }
- match event {
- Event::WindowEvent {
- event: glutin::WindowEvent::ReceivedCharacter(ch),
- ..
- } => self.handle_received_character(ch),
- Event::WindowEvent {
- event:
- glutin::WindowEvent::KeyboardInput {
- input,
- ..
- },
- ..
- } => self.handle_keyboard_input(input),
- Event::WindowEvent {
- event: glutin::WindowEvent::MouseInput { state, button, .. },
- ..
- } => {
- if button == MouseButton::Left || button == MouseButton::Right {
- self.handle_mouse(button, state, self.mouse_pos.get());
- }
- },
- Event::WindowEvent {
- event: glutin::WindowEvent::CursorMoved { position, .. },
- ..
- } => {
- let pos = position.to_physical(self.device_hidpi_factor().get() as f64);
- let (x, y): (i32, i32) = pos.into();
- self.mouse_pos.set(TypedPoint2D::new(x, y));
- self.event_queue
- .borrow_mut()
- .push(WindowEvent::MouseWindowMoveEventClass(TypedPoint2D::new(
- x as f32, y as f32,
- )));
- },
- Event::WindowEvent {
- event: glutin::WindowEvent::MouseWheel { delta, phase, .. },
- ..
- } => {
- let (mut dx, mut dy) = match delta {
- MouseScrollDelta::LineDelta(dx, dy) => (dx, dy * LINE_HEIGHT),
- MouseScrollDelta::PixelDelta(position) => {
- let position =
- position.to_physical(self.device_hidpi_factor().get() as f64);
- (position.x as f32, position.y as f32)
- },
- };
- // Scroll events snap to the major axis of movement, with vertical
- // preferred over horizontal.
- if dy.abs() >= dx.abs() {
- dx = 0.0;
- } else {
- dy = 0.0;
- }
-
- let scroll_location = ScrollLocation::Delta(TypedVector2D::new(dx, dy));
- let phase = winit_phase_to_touch_event_type(phase);
- let event = WindowEvent::Scroll(scroll_location, self.mouse_pos.get(), phase);
- self.event_queue.borrow_mut().push(event);
- },
- Event::WindowEvent {
- event: glutin::WindowEvent::Touch(touch),
- ..
- } => {
- use servo::script_traits::TouchId;
-
- let phase = winit_phase_to_touch_event_type(touch.phase);
- let id = TouchId(touch.id as i32);
- let position = touch
- .location
- .to_physical(self.device_hidpi_factor().get() as f64);
- let point = TypedPoint2D::new(position.x as f32, position.y as f32);
- self.event_queue
- .borrow_mut()
- .push(WindowEvent::Touch(phase, id, point));
- },
- Event::WindowEvent {
- event: glutin::WindowEvent::Refresh,
- ..
- } => self.event_queue.borrow_mut().push(WindowEvent::Refresh),
- Event::WindowEvent {
- event: glutin::WindowEvent::CloseRequested,
- ..
- } => {
- self.event_queue.borrow_mut().push(WindowEvent::Quit);
- },
- Event::WindowEvent {
- event: glutin::WindowEvent::Resized(size),
- ..
- } => {
- // size is DeviceIndependentPixel.
- // window.resize() takes DevicePixel.
- if let WindowKind::Window(ref window, _) = self.kind {
- let size = size.to_physical(self.device_hidpi_factor().get() as f64);
- window.resize(size);
- }
- // window.set_inner_size() takes DeviceIndependentPixel.
- let (width, height) = size.into();
- let new_size = TypedSize2D::new(width, height);
- if self.inner_size.get() != new_size {
- self.inner_size.set(new_size);
- self.event_queue.borrow_mut().push(WindowEvent::Resize);
- }
- },
- Event::Suspended(suspended) => {
- self.suspended.set(suspended);
- if !suspended {
- self.event_queue.borrow_mut().push(WindowEvent::Idle);
- }
- },
- Event::Awakened => {
- self.event_queue.borrow_mut().push(WindowEvent::Idle);
- },
- _ => {},
- }
- }
-
- /// Helper function to handle a click
- fn handle_mouse(
- &self,
- button: glutin::MouseButton,
- action: glutin::ElementState,
- coords: TypedPoint2D<i32, DevicePixel>,
- ) {
- use servo::script_traits::MouseButton;
-
- let max_pixel_dist = 10.0 * self.servo_hidpi_factor().get();
- let event = match action {
- ElementState::Pressed => {
- self.mouse_down_point.set(coords);
- self.mouse_down_button.set(Some(button));
- MouseWindowEvent::MouseDown(MouseButton::Left, coords.to_f32())
- },
- ElementState::Released => {
- let mouse_up_event = MouseWindowEvent::MouseUp(MouseButton::Left, coords.to_f32());
- match self.mouse_down_button.get() {
- None => mouse_up_event,
- Some(but) if button == but => {
- let pixel_dist = self.mouse_down_point.get() - coords;
- let pixel_dist =
- ((pixel_dist.x * pixel_dist.x + pixel_dist.y * pixel_dist.y) as f32)
- .sqrt();
- if pixel_dist < max_pixel_dist {
- self.event_queue
- .borrow_mut()
- .push(WindowEvent::MouseWindowEventClass(mouse_up_event));
- MouseWindowEvent::Click(MouseButton::Left, coords.to_f32())
- } else {
- mouse_up_event
- }
- },
- Some(_) => mouse_up_event,
- }
- },
- };
- self.event_queue
- .borrow_mut()
- .push(WindowEvent::MouseWindowEventClass(event));
- }
-
- fn device_hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> {
- match self.kind {
- WindowKind::Window(ref window, ..) => TypedScale::new(window.get_hidpi_factor() as f32),
- WindowKind::Headless(..) => TypedScale::new(1.0),
- }
- }
-
- fn servo_hidpi_factor(&self) -> TypedScale<f32, DeviceIndependentPixel, DevicePixel> {
- match opts::get().device_pixels_per_px {
- Some(device_pixels_per_px) => TypedScale::new(device_pixels_per_px),
- _ => match opts::get().output_file {
- Some(_) => TypedScale::new(1.0),
- None => self.device_hidpi_factor(),
- },
- }
- }
-
- pub fn set_cursor(&self, cursor: Cursor) {
- match self.kind {
- WindowKind::Window(ref window, ..) => {
- use glutin::MouseCursor;
-
- let winit_cursor = match cursor {
- Cursor::Default => MouseCursor::Default,
- Cursor::Pointer => MouseCursor::Hand,
- Cursor::ContextMenu => MouseCursor::ContextMenu,
- Cursor::Help => MouseCursor::Help,
- Cursor::Progress => MouseCursor::Progress,
- Cursor::Wait => MouseCursor::Wait,
- Cursor::Cell => MouseCursor::Cell,
- Cursor::Crosshair => MouseCursor::Crosshair,
- Cursor::Text => MouseCursor::Text,
- Cursor::VerticalText => MouseCursor::VerticalText,
- Cursor::Alias => MouseCursor::Alias,
- Cursor::Copy => MouseCursor::Copy,
- Cursor::Move => MouseCursor::Move,
- Cursor::NoDrop => MouseCursor::NoDrop,
- Cursor::NotAllowed => MouseCursor::NotAllowed,
- Cursor::Grab => MouseCursor::Grab,
- Cursor::Grabbing => MouseCursor::Grabbing,
- Cursor::EResize => MouseCursor::EResize,
- Cursor::NResize => MouseCursor::NResize,
- Cursor::NeResize => MouseCursor::NeResize,
- Cursor::NwResize => MouseCursor::NwResize,
- Cursor::SResize => MouseCursor::SResize,
- Cursor::SeResize => MouseCursor::SeResize,
- Cursor::SwResize => MouseCursor::SwResize,
- Cursor::WResize => MouseCursor::WResize,
- Cursor::EwResize => MouseCursor::EwResize,
- Cursor::NsResize => MouseCursor::NsResize,
- Cursor::NeswResize => MouseCursor::NeswResize,
- Cursor::NwseResize => MouseCursor::NwseResize,
- Cursor::ColResize => MouseCursor::ColResize,
- Cursor::RowResize => MouseCursor::RowResize,
- Cursor::AllScroll => MouseCursor::AllScroll,
- Cursor::ZoomIn => MouseCursor::ZoomIn,
- Cursor::ZoomOut => MouseCursor::ZoomOut,
- _ => MouseCursor::Default,
- };
- window.set_cursor(winit_cursor);
- },
- WindowKind::Headless(..) => {},
- }
- }
-}
-
-impl WindowMethods for Window {
- fn gl(&self) -> Rc<dyn gl::Gl> {
- self.gl.clone()
- }
-
- fn get_coordinates(&self) -> EmbedderCoordinates {
- match self.kind {
- WindowKind::Window(ref window, _) => {
- // TODO(ajeffrey): can this fail?
- let dpr = self.device_hidpi_factor();
- let LogicalSize { width, height } = window
- .get_outer_size()
- .expect("Failed to get window outer size.");
- let LogicalPosition { x, y } = window
- .get_position()
- .unwrap_or(LogicalPosition::new(0., 0.));
- let win_size = (TypedSize2D::new(width as f32, height as f32) * dpr).to_i32();
- let win_origin = (TypedPoint2D::new(x as f32, y as f32) * dpr).to_i32();
- let screen = (self.screen_size.to_f32() * dpr).to_i32();
-
- let LogicalSize { width, height } = window
- .get_inner_size()
- .expect("Failed to get window inner size.");
- let inner_size = (TypedSize2D::new(width as f32, height as f32) * dpr).to_i32();
- let viewport = DeviceIntRect::new(TypedPoint2D::zero(), inner_size);
- let framebuffer = FramebufferIntSize::from_untyped(&viewport.size.to_untyped());
-
- EmbedderCoordinates {
- viewport,
- framebuffer,
- window: (win_size, win_origin),
- screen: screen,
- // FIXME: Glutin doesn't have API for available size. Fallback to screen size
- screen_avail: screen,
- hidpi_factor: self.servo_hidpi_factor(),
- }
- },
- WindowKind::Headless(ref context) => {
- let dpr = self.servo_hidpi_factor();
- let size =
- (TypedSize2D::new(context.width, context.height).to_f32() * dpr).to_i32();
- let viewport = DeviceIntRect::new(TypedPoint2D::zero(), size);
- let framebuffer = FramebufferIntSize::from_untyped(&size.to_untyped());
- EmbedderCoordinates {
- viewport,
- framebuffer,
- window: (size, TypedPoint2D::zero()),
- screen: size,
- screen_avail: size,
- hidpi_factor: dpr,
- }
- },
- }
- }
-
- fn present(&self) {
- match self.kind {
- WindowKind::Window(ref window, ..) => {
- if let Err(err) = window.swap_buffers() {
- warn!("Failed to swap window buffers ({}).", err);
- }
- },
- WindowKind::Headless(..) => {},
- }
- }
-
- fn create_event_loop_waker(&self) -> Box<dyn EventLoopWaker> {
- struct GlutinEventLoopWaker {
- proxy: Option<Arc<glutin::EventsLoopProxy>>,
- }
- impl GlutinEventLoopWaker {
- fn new(window: &Window) -> GlutinEventLoopWaker {
- let proxy = match window.kind {
- WindowKind::Window(_, ref events_loop) => {
- Some(Arc::new(events_loop.borrow().create_proxy()))
- },
- WindowKind::Headless(..) => None,
- };
- GlutinEventLoopWaker { proxy }
- }
- }
- impl EventLoopWaker for GlutinEventLoopWaker {
- fn wake(&self) {
- // kick the OS event loop awake.
- if let Some(ref proxy) = self.proxy {
- if let Err(err) = proxy.wakeup() {
- warn!("Failed to wake up event loop ({}).", err);
- }
- }
- }
- fn clone(&self) -> Box<dyn EventLoopWaker + Send> {
- Box::new(GlutinEventLoopWaker {
- proxy: self.proxy.clone(),
- })
- }
- }
-
- Box::new(GlutinEventLoopWaker::new(&self))
- }
-
- fn set_animation_state(&self, state: AnimationState) {
- self.animation_state.set(state);
- }
-
- fn prepare_for_composite(&self) -> bool {
- if let WindowKind::Window(ref window, ..) = self.kind {
- if let Err(err) = unsafe { window.context().make_current() } {
- warn!("Couldn't make window current: {}", err);
- }
- };
- true
- }
-
- fn register_vr_services(
- &self,
- services: &mut VRServiceManager,
- heartbeats: &mut Vec<Box<WebVRMainThreadHeartbeat>>
- ) {
- if pref!(dom.webvr.test) {
- warn!("Creating test VR display");
- // TODO: support dom.webvr.test in headless environments
- if let WindowKind::Window(_, ref events_loop) = self.kind {
- // This is safe, because register_vr_services is called from the main thread.
- let name = String::from("Test VR Display");
- let size = self.inner_size.get().to_f64();
- let size = LogicalSize::new(size.width, size.height);
- let mut window_builder = glutin::WindowBuilder::new()
- .with_title(name.clone())
- .with_dimensions(size)
- .with_visibility(false)
- .with_multitouch();
- window_builder = builder_with_platform_options(window_builder);
- let context_builder = ContextBuilder::new()
- .with_gl(Window::gl_version())
- .with_vsync(false); // Assume the browser vsync is the same as the test VR window vsync
- let gl_window = GlWindow::new(window_builder, context_builder, &*events_loop.borrow())
- .expect("Failed to create window.");
- let gl = self.gl.clone();
- let (service, heartbeat) = GlWindowVRService::new(name, gl_window, gl);
-
- services.register(Box::new(service));
- heartbeats.push(Box::new(heartbeat));
- }
- }
- }
-}
-
-fn winit_phase_to_touch_event_type(phase: TouchPhase) -> TouchEventType {
- match phase {
- TouchPhase::Started => TouchEventType::Down,
- TouchPhase::Moved => TouchEventType::Move,
- TouchPhase::Ended => TouchEventType::Up,
- TouchPhase::Cancelled => TouchEventType::Cancel,
- }
-}
-
-#[cfg(any(target_os = "linux", target_os = "windows"))]
-fn load_icon(icon_bytes: &[u8]) -> Icon {
- let (icon_rgba, icon_width, icon_height) = {
- use image::{GenericImageView, Pixel};
- let image = image::load_from_memory(icon_bytes).expect("Failed to load icon");;
- let (width, height) = image.dimensions();
- let mut rgba = Vec::with_capacity((width * height) as usize * 4);
- for (_, _, pixel) in image.pixels() {
- rgba.extend_from_slice(&pixel.to_rgba().data);
- }
- (rgba, width, height)
- };
- Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to load icon")
-}
diff --git a/python/servo/build_commands.py b/python/servo/build_commands.py
index d516f1639dc..512fdfe4977 100644
--- a/python/servo/build_commands.py
+++ b/python/servo/build_commands.py
@@ -761,4 +761,4 @@ class MachCommands(CommandBase):
opts += ["-v"]
opts += params
return check_call(["cargo", "clean"] + opts,
- env=self.build_env(), cwd=self.ports_servo_crate(), verbose=verbose)
+ env=self.build_env(), cwd=self.ports_glutin_crate(), verbose=verbose)
diff --git a/python/servo/command_base.py b/python/servo/command_base.py
index 2d159df3773..ac9789ca389 100644
--- a/python/servo/command_base.py
+++ b/python/servo/command_base.py
@@ -723,20 +723,20 @@ install them, let us know by filing a bug!")
return env
- def ports_servo_crate(self):
- return path.join(self.context.topdir, "ports", "servo")
+ def ports_glutin_crate(self):
+ return path.join(self.context.topdir, "ports", "glutin")
def add_manifest_path(self, args, android=False, libsimpleservo=False):
if "--manifest-path" not in args:
if libsimpleservo or android:
manifest = self.ports_libsimpleservo_manifest(android)
else:
- manifest = self.ports_servo_manifest()
+ manifest = self.ports_glutin_manifest()
args.append("--manifest-path")
args.append(manifest)
- def ports_servo_manifest(self):
- return path.join(self.context.topdir, "ports", "servo", "Cargo.toml")
+ def ports_glutin_manifest(self):
+ return path.join(self.context.topdir, "ports", "glutin", "Cargo.toml")
def ports_libsimpleservo_manifest(self, android=False):
if android:
diff --git a/python/servo/post_build_commands.py b/python/servo/post_build_commands.py
index 4050f33f932..4d27475f74b 100644
--- a/python/servo/post_build_commands.py
+++ b/python/servo/post_build_commands.py
@@ -265,7 +265,7 @@ class PostBuildCommands(CommandBase):
copy2(full_name, destination)
returncode = self.call_rustup_run(
- ["cargo", "doc", "--manifest-path", self.ports_servo_manifest()] + params,
+ ["cargo", "doc", "--manifest-path", self.ports_glutin_manifest()] + params,
env=self.build_env())
if returncode:
return returncode
diff --git a/python/servo/testing_commands.py b/python/servo/testing_commands.py
index c8bb5e7511c..f4b324d4c62 100644
--- a/python/servo/testing_commands.py
+++ b/python/servo/testing_commands.py
@@ -279,7 +279,7 @@ class MachCommands(CommandBase):
features = self.servo_features()
if len(packages) > 0 or len(in_crate_packages) > 0:
- args = ["cargo", "bench" if bench else "test", "--manifest-path", self.ports_servo_manifest()]
+ args = ["cargo", "bench" if bench else "test", "--manifest-path", self.ports_glutin_manifest()]
for crate in packages:
args += ["-p", "%s_tests" % crate]
for crate in in_crate_packages: