aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGlenn Watson <gw@intuitionlibrary.com>2015-01-22 09:08:39 +1000
committerGlenn Watson <gw@intuitionlibrary.com>2015-01-23 06:09:25 +1000
commit0f525d908da229218c88a30cdaa837d492f802be (patch)
tree1f7717044738f400542521b942156799e576f43a
parent59bca2962c19f653eec835fc54caf1a3eadcb906 (diff)
downloadservo-0f525d908da229218c88a30cdaa837d492f802be.tar.gz
servo-0f525d908da229218c88a30cdaa837d492f802be.zip
Change glutin headless mode to be a build config, as it breaks some Linux distros linking to both.
The majority of this change is simply re-arranging the code in the glutin port so that the windowed/headless code is configured at build time rather than runtime. There shouldn't be any functional difference as a result of this change.
-rw-r--r--components/servo/Cargo.lock2
-rw-r--r--components/servo/Cargo.toml4
-rw-r--r--components/util/opts.rs20
-rw-r--r--ports/cef/Cargo.lock2
-rw-r--r--ports/cef/core.rs2
-rw-r--r--ports/glutin/Cargo.toml5
-rw-r--r--ports/glutin/lib.rs2
-rw-r--r--ports/glutin/window.rs848
-rw-r--r--python/servo/build_commands.py15
-rw-r--r--tests/reftest.rs3
10 files changed, 458 insertions, 445 deletions
diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock
index eebb40319ad..166ab14a397 100644
--- a/components/servo/Cargo.lock
+++ b/components/servo/Cargo.lock
@@ -335,7 +335,7 @@ dependencies = [
[[package]]
name = "glutin"
version = "0.0.2"
-source = "git+https://github.com/servo/glutin?ref=servo#db27370a1cbafcbfcaeee52a44076a61b3e0573c"
+source = "git+https://github.com/servo/glutin?ref=servo#ec6b4d0fff12ef607db422508ae005ba91406f5b"
dependencies = [
"android_glue 0.0.1 (git+https://github.com/servo/android-rs-glue?ref=servo)",
"cocoa 0.1.1 (git+https://github.com/servo/rust-cocoa)",
diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml
index f42178bb90f..2b4b03f5fa5 100644
--- a/components/servo/Cargo.toml
+++ b/components/servo/Cargo.toml
@@ -27,8 +27,10 @@ path = "../../tests/contenttest.rs"
harness = false
[features]
-default = ["glutin_app"]
+default = ["glutin_app", "window"]
glfw = ["glfw_app"]
+window = ["glutin_app/window"]
+headless = ["glutin_app/headless"]
[dependencies.compositing]
path = "../compositing"
diff --git a/components/util/opts.rs b/components/util/opts.rs
index 150c386aa23..1ed2eae0608 100644
--- a/components/util/opts.rs
+++ b/components/util/opts.rs
@@ -11,7 +11,6 @@ use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use layers::geometry::DevicePixel;
use getopts;
-use std::borrow::ToOwned;
use std::collections::HashSet;
use std::cmp;
use std::io;
@@ -20,12 +19,6 @@ use std::os;
use std::ptr;
use std::rt;
-#[deriving(Clone, Copy)]
-pub enum RenderApi {
- OpenGL,
- Mesa,
-}
-
/// Global flags for Servo, currently set on the command line.
#[deriving(Clone)]
pub struct Opts {
@@ -113,8 +106,6 @@ pub struct Opts {
/// Whether to show an error when display list geometry escapes flow overflow regions.
pub validate_display_list_geometry: bool,
- pub render_api: RenderApi,
-
/// A specific path to find required resources (such as user-agent.css).
pub resources_path: Option<String>,
}
@@ -183,7 +174,6 @@ pub fn default_opts() -> Opts {
dump_flow_tree: false,
validate_display_list_geometry: false,
profile_tasks: false,
- render_api: RenderApi::OpenGL,
resources_path: None,
}
}
@@ -303,15 +293,6 @@ pub fn from_cmdline_args(args: &[String]) -> bool {
}
};
- let render_api = match opt_match.opt_str("r").unwrap_or("gl".to_owned()).as_slice() {
- "mesa" => RenderApi::Mesa,
- "gl" => RenderApi::OpenGL,
- _ => {
- args_fail("Unknown render api specified");
- return false;
- }
- };
-
let opts = Opts {
urls: urls,
n_paint_threads: n_paint_threads,
@@ -337,7 +318,6 @@ pub fn from_cmdline_args(args: &[String]) -> bool {
enable_text_antialiasing: !debug_options.contains(&"disable-text-aa"),
dump_flow_tree: debug_options.contains(&"dump-flow-tree"),
validate_display_list_geometry: debug_options.contains(&"validate-display-list-geometry"),
- render_api: render_api,
resources_path: opt_match.opt_str("resources-path"),
};
diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock
index f63aacb1d1d..29c153fe8d2 100644
--- a/ports/cef/Cargo.lock
+++ b/ports/cef/Cargo.lock
@@ -314,7 +314,7 @@ dependencies = [
[[package]]
name = "glutin"
version = "0.0.2"
-source = "git+https://github.com/servo/glutin?ref=servo#db27370a1cbafcbfcaeee52a44076a61b3e0573c"
+source = "git+https://github.com/servo/glutin?ref=servo#ec6b4d0fff12ef607db422508ae005ba91406f5b"
dependencies = [
"android_glue 0.0.1 (git+https://github.com/servo/android-rs-glue?ref=servo)",
"cocoa 0.1.1 (git+https://github.com/servo/rust-cocoa)",
diff --git a/ports/cef/core.rs b/ports/cef/core.rs
index 5a99d9d217c..f4dd40a5e6e 100644
--- a/ports/cef/core.rs
+++ b/ports/cef/core.rs
@@ -11,7 +11,6 @@ use libc::{c_char, c_int, c_void};
use rustrt::local::Local;
use rustrt::task;
use servo_util::opts;
-use servo_util::opts::RenderApi;
use std::borrow::ToOwned;
use std::c_str::CString;
use std::rt;
@@ -105,7 +104,6 @@ pub extern "C" fn cef_initialize(args: *const cef_main_args_t,
user_agent: None,
dump_flow_tree: false,
validate_display_list_geometry: false,
- render_api: RenderApi::OpenGL,
resources_path: resources_path(),
});
diff --git a/ports/glutin/Cargo.toml b/ports/glutin/Cargo.toml
index 57d89f5949b..787b49136f8 100644
--- a/ports/glutin/Cargo.toml
+++ b/ports/glutin/Cargo.toml
@@ -7,6 +7,10 @@ authors = ["The Servo Project Developers"]
name = "glutin_app"
path = "lib.rs"
+[features]
+window = ["glutin/window"]
+headless = ["glutin/headless"]
+
[dependencies.compositing]
path = "../../components/compositing"
@@ -25,7 +29,6 @@ path = "../../components/util"
[dependencies.glutin]
git = "https://github.com/servo/glutin"
branch = "servo"
-features = ["window", "headless"]
[dependencies.gleam]
git = "https://github.com/servo/gleam"
diff --git a/ports/glutin/lib.rs b/ports/glutin/lib.rs
index 1e1aca93602..a7e6f8b5b22 100644
--- a/ports/glutin/lib.rs
+++ b/ports/glutin/lib.rs
@@ -40,5 +40,5 @@ pub fn create_window() -> Rc<Window> {
let size = opts.initial_window_size.as_f32() * scale_factor;
// Open a window.
- Window::new(foreground, size.as_uint(), opts.render_api)
+ Window::new(foreground, size.as_uint())
}
diff --git a/ports/glutin/window.rs b/ports/glutin/window.rs
index 52d404f7e87..188ddb7c8f1 100644
--- a/ports/glutin/window.rs
+++ b/ports/glutin/window.rs
@@ -5,57 +5,46 @@
//! A windowing implementation using glutin.
use compositing::compositor_task::{mod, CompositorProxy, CompositorReceiver};
-use compositing::windowing::WindowNavigateMsg;
-use compositing::windowing::{MouseWindowEvent, WindowEvent, WindowMethods};
-use geom::point::{Point2D, TypedPoint2D};
+use compositing::windowing::{WindowEvent, WindowMethods};
use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
+use gleam::gl;
+use glutin;
use layers::geometry::DevicePixel;
use layers::platform::surface::NativeGraphicsMetadata;
use msg::constellation_msg;
-use msg::constellation_msg::{Key, KeyState, CONTROL, SHIFT, ALT};
+use msg::constellation_msg::Key;
use msg::compositor_msg::{PaintState, ReadyState};
use msg::constellation_msg::LoadData;
+use NestedEventLoopListener;
+use std::rc::Rc;
+use util::cursor::Cursor;
+use util::geometry::ScreenPx;
+
+#[cfg(feature = "window")]
+use compositing::windowing::{MouseWindowEvent, WindowNavigateMsg};
+#[cfg(feature = "window")]
+use geom::point::{Point2D, TypedPoint2D};
+#[cfg(feature = "window")]
+use glutin::{ElementState, Event, MouseButton, VirtualKeyCode};
+#[cfg(feature = "window")]
+use msg::constellation_msg::{KeyState, CONTROL, SHIFT, ALT};
+#[cfg(feature = "window")]
use std::cell::{Cell, RefCell};
+#[cfg(feature = "window")]
use std::num::Float;
-use std::rc::Rc;
+#[cfg(feature = "window")]
use time::{mod, Timespec};
-use util::geometry::ScreenPx;
+#[cfg(feature = "window")]
use util::opts;
-use util::opts::RenderApi;
-use gleam::gl;
-use glutin;
-use glutin::{ElementState, Event, MouseButton, VirtualKeyCode};
-use NestedEventLoopListener;
-use util::cursor::Cursor;
-#[cfg(target_os="linux")]
+#[cfg(all(feature = "headless", target_os="linux"))]
use std::ptr;
-struct HeadlessContext {
- #[allow(dead_code)]
- context: glutin::HeadlessContext,
- size: TypedSize2D<DevicePixel, uint>,
-}
-
-enum WindowHandle {
- Windowed(glutin::Window),
- Headless(HeadlessContext),
-}
-
+#[cfg(feature = "window")]
static mut g_nested_event_loop_listener: Option<*mut (NestedEventLoopListener + 'static)> = None;
-fn nested_window_resize(width: uint, height: uint) {
- unsafe {
- match g_nested_event_loop_listener {
- None => {}
- Some(listener) => {
- (*listener).handle_event_from_nested_event_loop(WindowEvent::Resize(TypedSize2D(width, height)));
- }
- }
- }
-}
-
+#[cfg(feature = "window")]
bitflags!(
#[deriving(Show, Copy)]
flags KeyModifiers: u8 {
@@ -69,8 +58,9 @@ bitflags!(
)
/// The type of a window.
+#[cfg(feature = "window")]
pub struct Window {
- glutin: WindowHandle,
+ window: glutin::Window,
mouse_down_button: Cell<Option<glutin::MouseButton>>,
mouse_down_point: Cell<Point2D<int>>,
@@ -84,67 +74,24 @@ pub struct Window {
last_title_set_time: Cell<Timespec>,
}
-#[cfg(not(target_os="android"))]
-fn load_gl_functions(glutin: &WindowHandle) {
- match glutin {
- &WindowHandle::Windowed(ref window) => gl::load_with(|s| window.get_proc_address(s)),
- &WindowHandle::Headless(ref headless) => gl::load_with(|s| headless.context.get_proc_address(s)),
- }
-}
-
-#[cfg(target_os="android")]
-fn load_gl_functions(_glutin: &WindowHandle) {
-}
-
-#[cfg(not(target_os="android"))]
-fn gl_version() -> (uint, uint) {
- (3, 0)
-}
-
-#[cfg(target_os="android")]
-fn gl_version() -> (uint, uint) {
- (2, 0)
-}
-
+#[cfg(feature = "window")]
impl Window {
- /// Creates a new window.
- pub fn new(is_foreground: bool, size: TypedSize2D<DevicePixel, uint>, render_api: RenderApi)
- -> Rc<Window> {
-
- // Create the glutin window.
- let window_size = size.to_untyped();
-
- let glutin = match render_api {
- RenderApi::OpenGL => {
- let mut glutin_window = glutin::WindowBuilder::new()
- .with_title("Servo [glutin]".to_string())
- .with_dimensions(window_size.width, window_size.height)
- .with_gl_version(gl_version())
- .with_visibility(is_foreground)
- .build()
- .unwrap();
- unsafe { glutin_window.make_current() };
-
- glutin_window.set_window_resize_callback(Some(nested_window_resize));
- WindowHandle::Windowed(glutin_window)
- }
- RenderApi::Mesa => {
- let headless_builder = glutin::HeadlessRendererBuilder::new(window_size.width,
- window_size.height);
- let headless_context = headless_builder.build().unwrap();
- unsafe { headless_context.make_current() };
-
- WindowHandle::Headless(HeadlessContext {
- context: headless_context,
- size: size,
- })
- }
- };
+ pub fn new(is_foreground: bool, window_size: TypedSize2D<DevicePixel, uint>) -> Rc<Window> {
+ let mut glutin_window = glutin::WindowBuilder::new()
+ .with_title("Servo [glutin]".to_string())
+ .with_dimensions(window_size.to_untyped().width, window_size.to_untyped().height)
+ .with_gl_version(Window::gl_version())
+ .with_visibility(is_foreground)
+ .build()
+ .unwrap();
+ unsafe { glutin_window.make_current() };
+
+ glutin_window.set_window_resize_callback(Some(Window::nested_window_resize));
- load_gl_functions(&glutin);
+ Window::load_gl_functions(&glutin_window);
let window = Window {
- glutin: glutin,
+ window: glutin_window,
event_queue: RefCell::new(vec!()),
mouse_down_button: Cell::new(None),
mouse_down_point: Cell::new(Point2D(0, 0)),
@@ -164,303 +111,37 @@ impl Window {
Rc::new(window)
}
-}
-
-impl WindowMethods for Window {
- /// Returns the size of the window in hardware pixels.
- fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint> {
- let (width, height) = match self.glutin {
- WindowHandle::Windowed(ref window) => {
- let scale_factor = window.hidpi_factor() as uint;
- let (width, height) = window.get_inner_size().unwrap();
- Some((width * scale_factor, height * scale_factor))
- }
- WindowHandle::Headless(ref context) => Some((context.size.to_untyped().width,
- context.size.to_untyped().height)),
- }.unwrap();
- TypedSize2D(width as uint, height as uint)
- }
-
- /// Returns the size of the window in density-independent "px" units.
- fn size(&self) -> TypedSize2D<ScreenPx, f32> {
- // TODO: Handle hidpi
- let (width, height) = match self.glutin {
- WindowHandle::Windowed(ref window) => window.get_inner_size(),
- WindowHandle::Headless(ref context) => Some((context.size.to_untyped().width,
- context.size.to_untyped().height)),
- }.unwrap();
- TypedSize2D(width as f32, height as f32)
- }
-
- /// Presents the window to the screen (perhaps by page flipping).
- fn present(&self) {
- match self.glutin {
- WindowHandle::Windowed(ref window) => window.swap_buffers(),
- WindowHandle::Headless(_) => {},
- }
- }
-
- fn create_compositor_channel(window: &Option<Rc<Window>>)
- -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) {
- let (sender, receiver) = channel();
-
- let window_proxy = match window {
- &Some(ref window) => {
- match window.glutin {
- WindowHandle::Windowed(ref window) => Some(window.create_window_proxy()),
- WindowHandle::Headless(_) => None,
- }
- }
- &None => None,
- };
-
- (box GlutinCompositorProxy {
- sender: sender,
- window_proxy: window_proxy,
- } as Box<CompositorProxy+Send>,
- box receiver as Box<CompositorReceiver>)
- }
-
- /// Sets the ready state.
- fn set_ready_state(&self, ready_state: ReadyState) {
- self.ready_state.set(ready_state);
- self.update_window_title()
- }
-
- /// Sets the paint state.
- fn set_paint_state(&self, paint_state: PaintState) {
- self.paint_state.set(paint_state);
- self.update_window_title()
- }
-
- fn hidpi_factor(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
- match self.glutin {
- WindowHandle::Windowed(ref window) => ScaleFactor(window.hidpi_factor()),
- WindowHandle::Headless(_) => ScaleFactor(1.0),
- }
- }
-
- fn set_page_title(&self, _: Option<String>) {
- // TODO(gw)
- }
-
- fn set_page_load_data(&self, _: LoadData) {
- // TODO(gw)
- }
-
- fn load_end(&self) {
- // TODO(gw)
- }
-
- fn set_cursor(&self, _: Cursor) {
- // No-op. We could take over mouse handling ourselves and draw the cursor as an extra
- // layer with our own custom bitmaps or something, but it doesn't seem worth the
- // trouble.
- }
- fn prepare_for_composite(&self) -> bool {
- true
- }
-
- #[cfg(target_os="linux")]
- fn native_metadata(&self) -> NativeGraphicsMetadata {
- match self.glutin {
- WindowHandle::Windowed(ref window) => {
- NativeGraphicsMetadata {
- display: unsafe { window.platform_display() }
+ fn nested_window_resize(width: uint, height: uint) {
+ unsafe {
+ match g_nested_event_loop_listener {
+ None => {}
+ Some(listener) => {
+ (*listener).handle_event_from_nested_event_loop(WindowEvent::Resize(TypedSize2D(width, height)));
}
}
- WindowHandle::Headless(_) => {
- NativeGraphicsMetadata {
- display: ptr::null_mut()
- }
- },
}
}
- #[cfg(target_os="macos")]
- fn native_metadata(&self) -> NativeGraphicsMetadata {
- use cgl::{CGLGetCurrentContext, CGLGetPixelFormat};
- unsafe {
- NativeGraphicsMetadata {
- pixel_format: CGLGetPixelFormat(CGLGetCurrentContext()),
- }
- }
+ #[cfg(not(target_os="android"))]
+ fn gl_version() -> (uint, uint) {
+ (3, 0)
}
#[cfg(target_os="android")]
- fn native_metadata(&self) -> NativeGraphicsMetadata {
- use egl::egl::GetCurrentDisplay;
- NativeGraphicsMetadata {
- display: GetCurrentDisplay(),
- }
+ fn gl_version() -> (uint, uint) {
+ (2, 0)
}
- /// Helper function to handle keyboard events.
- fn handle_key(&self, key: Key, mods: constellation_msg::KeyModifiers) {
- match key {
- Key::Equal if mods.contains(CONTROL) => { // Ctrl-+
- self.event_queue.borrow_mut().push(WindowEvent::Zoom(1.1));
- }
- Key::Minus if mods.contains(CONTROL) => { // Ctrl--
- self.event_queue.borrow_mut().push(WindowEvent::Zoom(1.0/1.1));
- }
- Key::Backspace if mods.contains(SHIFT) => { // Shift-Backspace
- self.event_queue.borrow_mut().push(WindowEvent::Navigation(WindowNavigateMsg::Forward));
- }
- Key::Backspace => { // Backspace
- self.event_queue.borrow_mut().push(WindowEvent::Navigation(WindowNavigateMsg::Back));
- }
- Key::PageDown => {
- self.scroll_window(0.0, -self.framebuffer_size().as_f32().to_untyped().height);
- }
- Key::PageUp => {
- self.scroll_window(0.0, self.framebuffer_size().as_f32().to_untyped().height);
- }
- _ => {}
- }
+ #[cfg(not(target_os="android"))]
+ fn load_gl_functions(window: &glutin::Window) {
+ gl::load_with(|s| window.get_proc_address(s));
}
-}
-
-impl Window {
- /// Helper function to set the window title in accordance with the ready state.
- fn update_window_title(&self) {
- match self.glutin {
- WindowHandle::Windowed(ref window) => {
- let now = time::get_time();
- if now.sec == self.last_title_set_time.get().sec {
- return
- }
- self.last_title_set_time.set(now);
- match self.ready_state.get() {
- ReadyState::Blank => {
- window.set_title("blank - Servo [glutin]")
- }
- ReadyState::Loading => {
- window.set_title("Loading - Servo [glutin]")
- }
- ReadyState::PerformingLayout => {
- window.set_title("Performing Layout - Servo [glutin]")
- }
- ReadyState::FinishedLoading => {
- match self.paint_state.get() {
- PaintState::Painting => {
- window.set_title("Rendering - Servo [glutin]")
- }
- PaintState::Idle => {
- window.set_title("Servo [glutin]")
- }
- }
- }
- }
- }
- WindowHandle::Headless(_) => {},
- }
- }
-}
-
-fn glutin_mods_to_script_mods(modifiers: KeyModifiers) -> constellation_msg::KeyModifiers {
- let mut result = constellation_msg::KeyModifiers::from_bits(0).unwrap();
- if modifiers.intersects(LEFT_SHIFT | RIGHT_SHIFT) {
- result.insert(SHIFT);
- }
- if modifiers.intersects(LEFT_CONTROL | RIGHT_CONTROL) {
- result.insert(CONTROL);
- }
- if modifiers.intersects(LEFT_ALT | RIGHT_ALT) {
- result.insert(ALT);
- }
- result
-}
-
-fn glutin_key_to_script_key(key: glutin::VirtualKeyCode) -> Result<constellation_msg::Key, ()> {
- // TODO(negge): add more key mappings
- match key {
- VirtualKeyCode::A => Ok(Key::A),
- VirtualKeyCode::B => Ok(Key::B),
- VirtualKeyCode::C => Ok(Key::C),
- VirtualKeyCode::D => Ok(Key::D),
- VirtualKeyCode::E => Ok(Key::E),
- VirtualKeyCode::F => Ok(Key::F),
- VirtualKeyCode::G => Ok(Key::G),
- VirtualKeyCode::H => Ok(Key::H),
- VirtualKeyCode::I => Ok(Key::I),
- VirtualKeyCode::J => Ok(Key::J),
- VirtualKeyCode::K => Ok(Key::K),
- VirtualKeyCode::L => Ok(Key::L),
- VirtualKeyCode::M => Ok(Key::M),
- VirtualKeyCode::N => Ok(Key::N),
- VirtualKeyCode::O => Ok(Key::O),
- VirtualKeyCode::P => Ok(Key::P),
- VirtualKeyCode::Q => Ok(Key::Q),
- VirtualKeyCode::R => Ok(Key::R),
- VirtualKeyCode::S => Ok(Key::S),
- VirtualKeyCode::T => Ok(Key::T),
- VirtualKeyCode::U => Ok(Key::U),
- VirtualKeyCode::V => Ok(Key::V),
- VirtualKeyCode::W => Ok(Key::W),
- VirtualKeyCode::X => Ok(Key::X),
- VirtualKeyCode::Y => Ok(Key::Y),
- VirtualKeyCode::Z => Ok(Key::Z),
-
- VirtualKeyCode::Numpad0 => Ok(Key::Num0),
- VirtualKeyCode::Numpad1 => Ok(Key::Num1),
- VirtualKeyCode::Numpad2 => Ok(Key::Num2),
- VirtualKeyCode::Numpad3 => Ok(Key::Num3),
- VirtualKeyCode::Numpad4 => Ok(Key::Num4),
- VirtualKeyCode::Numpad5 => Ok(Key::Num5),
- VirtualKeyCode::Numpad6 => Ok(Key::Num6),
- VirtualKeyCode::Numpad7 => Ok(Key::Num7),
- VirtualKeyCode::Numpad8 => Ok(Key::Num8),
- VirtualKeyCode::Numpad9 => Ok(Key::Num9),
-
- VirtualKeyCode::Key0 => Ok(Key::Kp0),
- VirtualKeyCode::Key1 => Ok(Key::Kp1),
- VirtualKeyCode::Key2 => Ok(Key::Kp2),
- VirtualKeyCode::Key3 => Ok(Key::Kp3),
- VirtualKeyCode::Key4 => Ok(Key::Kp4),
- VirtualKeyCode::Key5 => Ok(Key::Kp5),
- VirtualKeyCode::Key6 => Ok(Key::Kp6),
- VirtualKeyCode::Key7 => Ok(Key::Kp7),
- VirtualKeyCode::Key8 => Ok(Key::Kp8),
- VirtualKeyCode::Key9 => Ok(Key::Kp9),
-
- VirtualKeyCode::Return => Ok(Key::Enter),
- VirtualKeyCode::Space => Ok(Key::Space),
- VirtualKeyCode::Escape => Ok(Key::Escape),
- VirtualKeyCode::Equals => Ok(Key::Equal),
- VirtualKeyCode::Minus => Ok(Key::Minus),
- VirtualKeyCode::Back => Ok(Key::Backspace),
- VirtualKeyCode::PageDown => Ok(Key::PageDown),
- VirtualKeyCode::PageUp => Ok(Key::PageUp),
-
- VirtualKeyCode::Insert => Ok(Key::Insert),
- VirtualKeyCode::Home => Ok(Key::Home),
- VirtualKeyCode::Delete => Ok(Key::Delete),
- VirtualKeyCode::End => Ok(Key::End),
-
- VirtualKeyCode::Left => Ok(Key::Left),
- VirtualKeyCode::Up => Ok(Key::Up),
- VirtualKeyCode::Right => Ok(Key::Right),
- VirtualKeyCode::Down => Ok(Key::Down),
-
- VirtualKeyCode::Apostrophe => Ok(Key::Apostrophe),
- VirtualKeyCode::Backslash => Ok(Key::Backslash),
- VirtualKeyCode::Comma => Ok(Key::Comma),
- VirtualKeyCode::Grave => Ok(Key::GraveAccent),
- VirtualKeyCode::LBracket => Ok(Key::LeftBracket),
- VirtualKeyCode::Period => Ok(Key::Period),
- VirtualKeyCode::RBracket => Ok(Key::RightBracket),
- VirtualKeyCode::Semicolon => Ok(Key::Semicolon),
- VirtualKeyCode::Slash => Ok(Key::Slash),
- VirtualKeyCode::Tab => Ok(Key::Tab),
-
- _ => Err(()),
+ #[cfg(target_os="android")]
+ fn load_gl_functions(_: &glutin::Window) {
}
-}
-impl Window {
fn handle_window_event(&self, event: glutin::Event) -> bool {
match event {
Event::KeyboardInput(element_state, _scan_code, virtual_key_code) => {
@@ -476,10 +157,10 @@ impl Window {
(_, VirtualKeyCode::RAlt) => self.toggle_modifier(RIGHT_ALT),
(ElementState::Pressed, VirtualKeyCode::Escape) => return true,
(ElementState::Pressed, key_code) => {
- match glutin_key_to_script_key(key_code) {
+ match Window::glutin_key_to_script_key(key_code) {
Ok(key) => {
let state = KeyState::Pressed;
- let modifiers = glutin_mods_to_script_mods(self.key_modifiers.get());
+ let modifiers = Window::glutin_mods_to_script_mods(self.key_modifiers.get());
self.event_queue.borrow_mut().push(WindowEvent::KeyEvent(key, state, modifiers));
}
_ => {}
@@ -574,6 +255,72 @@ impl Window {
self.event_queue.borrow_mut().push(WindowEvent::MouseWindowEventClass(event));
}
+ fn update_window_title(&self) {
+ let now = time::get_time();
+ if now.sec == self.last_title_set_time.get().sec {
+ return
+ }
+ self.last_title_set_time.set(now);
+
+ match self.ready_state.get() {
+ ReadyState::Blank => {
+ self.window.set_title("blank - Servo [glutin]")
+ }
+ ReadyState::Loading => {
+ self.window.set_title("Loading - Servo [glutin]")
+ }
+ ReadyState::PerformingLayout => {
+ self.window.set_title("Performing Layout - Servo [glutin]")
+ }
+ ReadyState::FinishedLoading => {
+ match self.paint_state.get() {
+ PaintState::Painting => {
+ self.window.set_title("Rendering - Servo [glutin]")
+ }
+ PaintState::Idle => {
+ self.window.set_title("Servo [glutin]")
+ }
+ }
+ }
+ }
+ }
+
+ pub fn wait_events(&self) -> WindowEvent {
+ {
+ let mut event_queue = self.event_queue.borrow_mut();
+ if !event_queue.is_empty() {
+ return event_queue.remove(0).unwrap();
+ }
+ }
+
+ let mut close_event = false;
+
+ // When writing to a file then exiting, use event
+ // polling so that we don't block on a GUI event
+ // such as mouse click.
+ if opts::get().output_file.is_some() {
+ for event in self.window.poll_events() {
+ close_event = self.handle_window_event(event);
+ if close_event {
+ break;
+ }
+ }
+ } else {
+ for event in self.window.wait_events() {
+ close_event = self.handle_window_event(event);
+ if close_event {
+ break;
+ }
+ }
+ }
+
+ if close_event || self.window.is_closed() {
+ WindowEvent::Quit
+ } else {
+ self.event_queue.borrow_mut().remove(0).unwrap_or(WindowEvent::Idle)
+ }
+ }
+
pub unsafe fn set_nested_event_loop_listener(
&self,
listener: *mut (NestedEventLoopListener + 'static)) {
@@ -584,48 +331,327 @@ impl Window {
g_nested_event_loop_listener = None
}
- pub fn wait_events(&self) -> WindowEvent {
- {
- let mut event_queue = self.event_queue.borrow_mut();
- if !event_queue.is_empty() {
- return event_queue.remove(0).unwrap();
+ fn glutin_key_to_script_key(key: glutin::VirtualKeyCode) -> Result<constellation_msg::Key, ()> {
+ // TODO(negge): add more key mappings
+ match key {
+ VirtualKeyCode::A => Ok(Key::A),
+ VirtualKeyCode::B => Ok(Key::B),
+ VirtualKeyCode::C => Ok(Key::C),
+ VirtualKeyCode::D => Ok(Key::D),
+ VirtualKeyCode::E => Ok(Key::E),
+ VirtualKeyCode::F => Ok(Key::F),
+ VirtualKeyCode::G => Ok(Key::G),
+ VirtualKeyCode::H => Ok(Key::H),
+ VirtualKeyCode::I => Ok(Key::I),
+ VirtualKeyCode::J => Ok(Key::J),
+ VirtualKeyCode::K => Ok(Key::K),
+ VirtualKeyCode::L => Ok(Key::L),
+ VirtualKeyCode::M => Ok(Key::M),
+ VirtualKeyCode::N => Ok(Key::N),
+ VirtualKeyCode::O => Ok(Key::O),
+ VirtualKeyCode::P => Ok(Key::P),
+ VirtualKeyCode::Q => Ok(Key::Q),
+ VirtualKeyCode::R => Ok(Key::R),
+ VirtualKeyCode::S => Ok(Key::S),
+ VirtualKeyCode::T => Ok(Key::T),
+ VirtualKeyCode::U => Ok(Key::U),
+ VirtualKeyCode::V => Ok(Key::V),
+ VirtualKeyCode::W => Ok(Key::W),
+ VirtualKeyCode::X => Ok(Key::X),
+ VirtualKeyCode::Y => Ok(Key::Y),
+ VirtualKeyCode::Z => Ok(Key::Z),
+
+ VirtualKeyCode::Numpad0 => Ok(Key::Num0),
+ VirtualKeyCode::Numpad1 => Ok(Key::Num1),
+ VirtualKeyCode::Numpad2 => Ok(Key::Num2),
+ VirtualKeyCode::Numpad3 => Ok(Key::Num3),
+ VirtualKeyCode::Numpad4 => Ok(Key::Num4),
+ VirtualKeyCode::Numpad5 => Ok(Key::Num5),
+ VirtualKeyCode::Numpad6 => Ok(Key::Num6),
+ VirtualKeyCode::Numpad7 => Ok(Key::Num7),
+ VirtualKeyCode::Numpad8 => Ok(Key::Num8),
+ VirtualKeyCode::Numpad9 => Ok(Key::Num9),
+
+ VirtualKeyCode::Key0 => Ok(Key::Kp0),
+ VirtualKeyCode::Key1 => Ok(Key::Kp1),
+ VirtualKeyCode::Key2 => Ok(Key::Kp2),
+ VirtualKeyCode::Key3 => Ok(Key::Kp3),
+ VirtualKeyCode::Key4 => Ok(Key::Kp4),
+ VirtualKeyCode::Key5 => Ok(Key::Kp5),
+ VirtualKeyCode::Key6 => Ok(Key::Kp6),
+ VirtualKeyCode::Key7 => Ok(Key::Kp7),
+ VirtualKeyCode::Key8 => Ok(Key::Kp8),
+ VirtualKeyCode::Key9 => Ok(Key::Kp9),
+
+ VirtualKeyCode::Return => Ok(Key::Enter),
+ VirtualKeyCode::Space => Ok(Key::Space),
+ VirtualKeyCode::Escape => Ok(Key::Escape),
+ VirtualKeyCode::Equals => Ok(Key::Equal),
+ VirtualKeyCode::Minus => Ok(Key::Minus),
+ VirtualKeyCode::Back => Ok(Key::Backspace),
+ VirtualKeyCode::PageDown => Ok(Key::PageDown),
+ VirtualKeyCode::PageUp => Ok(Key::PageUp),
+
+ VirtualKeyCode::Insert => Ok(Key::Insert),
+ VirtualKeyCode::Home => Ok(Key::Home),
+ VirtualKeyCode::Delete => Ok(Key::Delete),
+ VirtualKeyCode::End => Ok(Key::End),
+
+ VirtualKeyCode::Left => Ok(Key::Left),
+ VirtualKeyCode::Up => Ok(Key::Up),
+ VirtualKeyCode::Right => Ok(Key::Right),
+ VirtualKeyCode::Down => Ok(Key::Down),
+
+ VirtualKeyCode::Apostrophe => Ok(Key::Apostrophe),
+ VirtualKeyCode::Backslash => Ok(Key::Backslash),
+ VirtualKeyCode::Comma => Ok(Key::Comma),
+ VirtualKeyCode::Grave => Ok(Key::GraveAccent),
+ VirtualKeyCode::LBracket => Ok(Key::LeftBracket),
+ VirtualKeyCode::Period => Ok(Key::Period),
+ VirtualKeyCode::RBracket => Ok(Key::RightBracket),
+ VirtualKeyCode::Semicolon => Ok(Key::Semicolon),
+ VirtualKeyCode::Slash => Ok(Key::Slash),
+ VirtualKeyCode::Tab => Ok(Key::Tab),
+
+ _ => Err(()),
+ }
+ }
+
+ fn glutin_mods_to_script_mods(modifiers: KeyModifiers) -> constellation_msg::KeyModifiers {
+ let mut result = constellation_msg::KeyModifiers::from_bits(0).unwrap();
+ if modifiers.intersects(LEFT_SHIFT | RIGHT_SHIFT) {
+ result.insert(SHIFT);
+ }
+ if modifiers.intersects(LEFT_CONTROL | RIGHT_CONTROL) {
+ result.insert(CONTROL);
+ }
+ if modifiers.intersects(LEFT_ALT | RIGHT_ALT) {
+ result.insert(ALT);
+ }
+ result
+ }
+}
+
+#[cfg(feature = "window")]
+impl WindowMethods for Window {
+ fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint> {
+ let scale_factor = self.window.hidpi_factor() as uint;
+ let (width, height) = self.window.get_inner_size().unwrap();
+ TypedSize2D(width * scale_factor, height * scale_factor)
+ }
+
+ fn size(&self) -> TypedSize2D<ScreenPx, f32> {
+ let (width, height) = self.window.get_inner_size().unwrap();
+ TypedSize2D(width as f32, height as f32)
+ }
+
+ fn present(&self) {
+ self.window.swap_buffers()
+ }
+
+ fn create_compositor_channel(window: &Option<Rc<Window>>)
+ -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) {
+ let (sender, receiver) = channel();
+
+ let window_proxy = match window {
+ &Some(ref window) => Some(window.window.create_window_proxy()),
+ &None => None,
+ };
+
+ (box GlutinCompositorProxy {
+ sender: sender,
+ window_proxy: window_proxy,
+ } as Box<CompositorProxy+Send>,
+ box receiver as Box<CompositorReceiver>)
+ }
+
+ /// Sets the ready state.
+ fn set_ready_state(&self, ready_state: ReadyState) {
+ self.ready_state.set(ready_state);
+ self.update_window_title()
+ }
+
+ /// Sets the paint state.
+ fn set_paint_state(&self, paint_state: PaintState) {
+ self.paint_state.set(paint_state);
+ self.update_window_title()
+ }
+
+ fn hidpi_factor(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
+ ScaleFactor(self.window.hidpi_factor())
+ }
+
+ fn set_page_title(&self, _: Option<String>) {
+ }
+
+ fn set_page_load_data(&self, _: LoadData) {
+ }
+
+ fn load_end(&self) {
+ }
+
+ fn set_cursor(&self, _: Cursor) {
+ }
+
+ fn prepare_for_composite(&self) -> bool {
+ true
+ }
+
+ #[cfg(target_os="linux")]
+ fn native_metadata(&self) -> NativeGraphicsMetadata {
+ NativeGraphicsMetadata {
+ display: unsafe { self.window.platform_display() }
+ }
+ }
+
+ #[cfg(target_os="macos")]
+ fn native_metadata(&self) -> NativeGraphicsMetadata {
+ use cgl::{CGLGetCurrentContext, CGLGetPixelFormat};
+ unsafe {
+ NativeGraphicsMetadata {
+ pixel_format: CGLGetPixelFormat(CGLGetCurrentContext()),
}
}
+ }
- match self.glutin {
- WindowHandle::Windowed(ref window) => {
- let mut close_event = false;
-
- // When writing to a file then exiting, use event
- // polling so that we don't block on a GUI event
- // such as mouse click.
- if opts::get().output_file.is_some() {
- for event in window.poll_events() {
- close_event = self.handle_window_event(event);
- if close_event {
- break;
- }
- }
- } else {
- for event in window.wait_events() {
- close_event = self.handle_window_event(event);
- if close_event {
- break;
- }
- }
- }
+ #[cfg(target_os="android")]
+ fn native_metadata(&self) -> NativeGraphicsMetadata {
+ use egl::egl::GetCurrentDisplay;
+ NativeGraphicsMetadata {
+ display: GetCurrentDisplay(),
+ }
+ }
- if close_event || window.is_closed() {
- WindowEvent::Quit
- } else {
- self.event_queue.borrow_mut().remove(0).unwrap_or(WindowEvent::Idle)
- }
+ /// Helper function to handle keyboard events.
+ fn handle_key(&self, key: Key, mods: constellation_msg::KeyModifiers) {
+ match key {
+ Key::Equal if mods.contains(CONTROL) => { // Ctrl-+
+ self.event_queue.borrow_mut().push(WindowEvent::Zoom(1.1));
}
- WindowHandle::Headless(_) => {
- self.event_queue.borrow_mut().remove(0).unwrap_or(WindowEvent::Idle)
+ Key::Minus if mods.contains(CONTROL) => { // Ctrl--
+ self.event_queue.borrow_mut().push(WindowEvent::Zoom(1.0/1.1));
+ }
+ Key::Backspace if mods.contains(SHIFT) => { // Shift-Backspace
+ self.event_queue.borrow_mut().push(WindowEvent::Navigation(WindowNavigateMsg::Forward));
+ }
+ Key::Backspace => { // Backspace
+ self.event_queue.borrow_mut().push(WindowEvent::Navigation(WindowNavigateMsg::Back));
}
+ Key::PageDown => {
+ self.scroll_window(0.0, -self.framebuffer_size().as_f32().to_untyped().height);
+ }
+ Key::PageUp => {
+ self.scroll_window(0.0, self.framebuffer_size().as_f32().to_untyped().height);
+ }
+ _ => {}
+ }
+ }
+}
+
+/// The type of a window.
+#[cfg(feature = "headless")]
+pub struct Window {
+ #[allow(dead_code)]
+ context: glutin::HeadlessContext,
+ width: uint,
+ height: uint,
+}
+
+#[cfg(feature = "headless")]
+impl Window {
+ pub fn new(_is_foreground: bool, window_size: TypedSize2D<DevicePixel, uint>) -> Rc<Window> {
+ let window_size = window_size.to_untyped();
+ let headless_builder = glutin::HeadlessRendererBuilder::new(window_size.width,
+ window_size.height);
+ let headless_context = headless_builder.build().unwrap();
+ unsafe { headless_context.make_current() };
+
+ gl::load_with(|s| headless_context.get_proc_address(s));
+
+ let window = Window {
+ context: headless_context,
+ width: window_size.width,
+ height: window_size.height,
+ };
+
+ Rc::new(window)
+ }
+
+ pub fn wait_events(&self) -> WindowEvent {
+ WindowEvent::Idle
+ }
+
+ pub unsafe fn set_nested_event_loop_listener(
+ &self,
+ _listener: *mut (NestedEventLoopListener + 'static)) {
+ }
+
+ pub unsafe fn remove_nested_event_loop_listener(&self) {
+ }
+}
+
+#[cfg(feature = "headless")]
+impl WindowMethods for Window {
+ fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint> {
+ TypedSize2D(self.width, self.height)
+ }
+
+ fn size(&self) -> TypedSize2D<ScreenPx, f32> {
+ TypedSize2D(self.width as f32, self.height as f32)
+ }
+
+ fn present(&self) {
+ }
+
+ fn create_compositor_channel(_: &Option<Rc<Window>>)
+ -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) {
+ let (sender, receiver) = channel();
+
+ (box GlutinCompositorProxy {
+ sender: sender,
+ window_proxy: None,
+ } as Box<CompositorProxy+Send>,
+ box receiver as Box<CompositorReceiver>)
+ }
+
+ /// Sets the ready state.
+ fn set_ready_state(&self, _: ReadyState) {
+ }
+
+ /// Sets the paint state.
+ fn set_paint_state(&self, _: PaintState) {
+ }
+
+ fn hidpi_factor(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
+ ScaleFactor(1.0)
+ }
+
+ fn set_page_title(&self, _: Option<String>) {
+ }
+
+ fn set_page_load_data(&self, _: LoadData) {
+ }
+
+ fn load_end(&self) {
+ }
+
+ fn set_cursor(&self, _: Cursor) {
+ }
+
+ fn prepare_for_composite(&self) -> bool {
+ true
+ }
+
+ #[cfg(target_os="linux")]
+ fn native_metadata(&self) -> NativeGraphicsMetadata {
+ NativeGraphicsMetadata {
+ display: ptr::null_mut()
}
}
+
+ /// Helper function to handle keyboard events.
+ fn handle_key(&self, _: Key, _: constellation_msg::KeyModifiers) {
+ }
}
struct GlutinCompositorProxy {
diff --git a/python/servo/build_commands.py b/python/servo/build_commands.py
index 78d9fb51318..c4f6edc62b8 100644
--- a/python/servo/build_commands.py
+++ b/python/servo/build_commands.py
@@ -1,5 +1,6 @@
from __future__ import print_function, unicode_literals
+import os
import os.path as path
import subprocess
from time import time
@@ -12,6 +13,8 @@ from mach.decorators import (
from servo.command_base import CommandBase, cd
+def is_headless_build():
+ return int(os.getenv('SERVO_HEADLESS', 0)) == 1
@CommandProvider
class MachCommands(CommandBase):
@@ -69,6 +72,10 @@ class MachCommands(CommandBase):
if debug_mozjs or self.config["build"]["debug-mozjs"]:
features += ["script/debugmozjs"]
+ if is_headless_build():
+ opts += ["--no-default-features"]
+ features += ["glutin_app", "headless"]
+
if features:
opts += ["--features", "%s" % ' '.join(features)]
@@ -132,11 +139,11 @@ class MachCommands(CommandBase):
help='Number of jobs to run in parallel')
def build_tests(self, jobs=None):
self.ensure_bootstrapped()
- opts = []
- if jobs is not None:
- opts += ["-j", jobs]
+ args = ["cargo", "test", "--no-run"]
+ if is_headless_build():
+ args += ["--no-default-features", "--features", "glutin_app headless"]
return subprocess.call(
- ["cargo", "test", "--no-run"],
+ args,
env=self.build_env(), cwd=self.servo_crate())
@Command('clean',
diff --git a/tests/reftest.rs b/tests/reftest.rs
index 6c483d6d02a..92c84b28b55 100644
--- a/tests/reftest.rs
+++ b/tests/reftest.rs
@@ -276,9 +276,6 @@ fn capture(reftest: &Reftest, side: uint) -> (u32, u32, Vec<u8>) {
if reftest.experimental {
command.arg("--experimental");
}
- if cfg!(target_os = "linux") {
- command.args(["-r", "mesa"].as_slice());
- }
let retval = match command.status() {
Ok(status) => status,
Err(e) => panic!("failed to execute process: {}", e),