diff options
author | Glenn Watson <gw@intuitionlibrary.com> | 2015-01-22 09:08:39 +1000 |
---|---|---|
committer | Glenn Watson <gw@intuitionlibrary.com> | 2015-01-23 06:09:25 +1000 |
commit | 0f525d908da229218c88a30cdaa837d492f802be (patch) | |
tree | 1f7717044738f400542521b942156799e576f43a | |
parent | 59bca2962c19f653eec835fc54caf1a3eadcb906 (diff) | |
download | servo-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.lock | 2 | ||||
-rw-r--r-- | components/servo/Cargo.toml | 4 | ||||
-rw-r--r-- | components/util/opts.rs | 20 | ||||
-rw-r--r-- | ports/cef/Cargo.lock | 2 | ||||
-rw-r--r-- | ports/cef/core.rs | 2 | ||||
-rw-r--r-- | ports/glutin/Cargo.toml | 5 | ||||
-rw-r--r-- | ports/glutin/lib.rs | 2 | ||||
-rw-r--r-- | ports/glutin/window.rs | 848 | ||||
-rw-r--r-- | python/servo/build_commands.py | 15 | ||||
-rw-r--r-- | tests/reftest.rs | 3 |
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), |