diff options
author | Glenn Watson <gw@intuitionlibrary.com> | 2014-11-18 11:10:02 +1000 |
---|---|---|
committer | Glenn Watson <gw@intuitionlibrary.com> | 2014-11-19 08:31:34 +1000 |
commit | 0278920343bea47d9ba736830fa2032c483363a3 (patch) | |
tree | 4d105154bbd1c78f940e3fc143add134c83c8343 | |
parent | 64cc9ec6881add395b93341a32e90546b2f7211f (diff) | |
download | servo-0278920343bea47d9ba736830fa2032c483363a3.tar.gz servo-0278920343bea47d9ba736830fa2032c483363a3.zip |
Add glutin port (supported on Linux only currently).
Default build uses glfw, but glutin can be enabled via:
./mach cargo build --no-default-features --features=glutin
-rw-r--r-- | Cargo.lock | 50 | ||||
-rw-r--r-- | Cargo.toml | 5 | ||||
-rw-r--r-- | components/util/opts.rs | 22 | ||||
-rw-r--r-- | ports/android/glut_app/Cargo.lock | 3 | ||||
-rw-r--r-- | ports/cef/Cargo.lock | 4 | ||||
-rw-r--r-- | ports/cef/core.rs | 2 | ||||
-rw-r--r-- | ports/glfw/Cargo.toml | 3 | ||||
-rw-r--r-- | ports/glfw/window.rs | 10 | ||||
-rw-r--r-- | ports/glutin/Cargo.toml | 36 | ||||
-rw-r--r-- | ports/glutin/lib.rs | 46 | ||||
-rw-r--r-- | ports/glutin/window.rs | 479 | ||||
-rw-r--r-- | src/main.rs | 13 |
12 files changed, 659 insertions, 14 deletions
diff --git a/Cargo.lock b/Cargo.lock index 7e60010361e..d728d9e48b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,6 +5,7 @@ dependencies = [ "compositing 0.0.1", "gfx 0.0.1", "glfw_app 0.0.1", + "glutin_app 0.0.1", "green 0.0.1 (git+https://github.com/servo/green-rs?ref=servo)", "layout 0.0.1", "msg 0.0.1", @@ -25,6 +26,14 @@ dependencies = [ ] [[package]] +name = "android_glue" +version = "0.0.1" +source = "git+https://github.com/tomaka/android-rs-glue#fe9acb5bd465da1df4561e2bd4ebcc6d305134a4" +dependencies = [ + "compile_msg 0.1.1 (git+https://github.com/huonw/compile_msg)", +] + +[[package]] name = "azure" version = "0.1.0" source = "git+https://github.com/servo/rust-azure#64853170e435d9320f3fd828b9e7d16bc4c260a3" @@ -60,9 +69,19 @@ dependencies = [ [[package]] name = "cocoa" version = "0.1.1" +source = "git+https://github.com/DavidPartouche/rust-cocoa#0a951a6cdd5f1a175b929754c134f9443b105642" + +[[package]] +name = "cocoa" +version = "0.1.1" source = "git+https://github.com/servo/rust-cocoa#f926323d306401df33f528c9aeca8e582cad063b" [[package]] +name = "compile_msg" +version = "0.1.1" +source = "git+https://github.com/huonw/compile_msg#f526abe54b49642bc1e969e6c2af1411798b6776" + +[[package]] name = "compositing" version = "0.0.1" dependencies = [ @@ -279,6 +298,7 @@ dependencies = [ "cgl 0.0.1 (git+https://github.com/servo/rust-cgl)", "compositing 0.0.1", "geom 0.1.0 (git+https://github.com/servo/rust-geom)", + "gleam 0.0.1 (git+https://github.com/servo/gleam)", "glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)", "msg 0.0.1", @@ -286,6 +306,34 @@ dependencies = [ ] [[package]] +name = "glutin" +version = "0.0.2" +source = "git+https://github.com/tomaka/glutin#0dc5086efb29e84c48cd4dcc1da48ee9755842e3" +dependencies = [ + "android_glue 0.0.1 (git+https://github.com/tomaka/android-rs-glue)", + "cocoa 0.1.1 (git+https://github.com/DavidPartouche/rust-cocoa)", + "compile_msg 0.1.1 (git+https://github.com/huonw/compile_msg)", + "core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)", + "gl_common 0.0.1 (git+https://github.com/bjz/gl-rs.git)", + "gl_generator 0.0.1 (git+https://github.com/bjz/gl-rs.git)", +] + +[[package]] +name = "glutin_app" +version = "0.0.1" +dependencies = [ + "alert 0.1.0 (git+https://github.com/servo/rust-alert)", + "cgl 0.0.1 (git+https://github.com/servo/rust-cgl)", + "compositing 0.0.1", + "geom 0.1.0 (git+https://github.com/servo/rust-geom)", + "gleam 0.0.1 (git+https://github.com/servo/gleam)", + "glutin 0.0.2 (git+https://github.com/tomaka/glutin)", + "layers 0.1.0 (git+https://github.com/servo/rust-layers)", + "msg 0.0.1", + "util 0.0.1", +] + +[[package]] name = "glx" version = "0.0.1" source = "git+https://github.com/servo/rust-glx#7126ffa09fcfcc9f85f1406f3b5db729f5fdb7c3" @@ -358,7 +406,7 @@ source = "git+https://github.com/bjz/gl-rs.git#79cd3b3f9f19aa0e39f6af572fc8673a6 [[package]] name = "layers" version = "0.1.0" -source = "git+https://github.com/servo/rust-layers#84f6dd677aa681b03cfcbf89000d2c7b12684bd3" +source = "git+https://github.com/servo/rust-layers#0f6edd58b3b572f2aac97567b752c15db5fa1982" dependencies = [ "cgl 0.0.1 (git+https://github.com/servo/rust-cgl)", "core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)", diff --git a/Cargo.toml b/Cargo.toml index efc9d869c4d..9dca413e4a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ harness = false [features] default = ["glfw_app"] +glutin = ["glutin_app"] [dependencies.compositing] path = "components/compositing" @@ -52,6 +53,10 @@ path = "components/gfx" path = "ports/glfw" optional = true +[dependencies.glutin_app] +path = "ports/glutin" +optional = true + [dependencies.url] git = "https://github.com/servo/rust-url" diff --git a/components/util/opts.rs b/components/util/opts.rs index ab744ada5c8..f80980f22a2 100644 --- a/components/util/opts.rs +++ b/components/util/opts.rs @@ -19,6 +19,12 @@ use std::os; use std::ptr; use std::rt; +#[deriving(Clone)] +pub enum RenderApi { + OpenGL, + Mesa, +} + /// Global flags for Servo, currently set on the command line. #[deriving(Clone)] pub struct Opts { @@ -108,6 +114,8 @@ 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, } fn print_usage(app: &str, opts: &[getopts::OptGroup]) { @@ -175,6 +183,7 @@ fn default_opts() -> Opts { dump_flow_tree: false, validate_display_list_geometry: false, profile_tasks: false, + render_api: OpenGL, } } @@ -201,7 +210,8 @@ pub fn from_cmdline_args(args: &[String]) -> bool { getopts::optopt("", "resolution", "Set window resolution.", "800x600"), getopts::optopt("u", "user-agent", "Set custom user agent string", "NCSA Mosaic/1.0 (X11;SunOS 4.1.4 sun4m)"), getopts::optopt("Z", "debug", "A comma-separated string of debug options. Pass help to show available options.", ""), - getopts::optflag("h", "help", "Print this message") + getopts::optflag("h", "help", "Print this message"), + getopts::optopt("r", "render-api", "Set the rendering API to use", "gl|mesa"), ); let opt_match = match getopts::getopts(args, opts.as_slice()) { @@ -291,6 +301,15 @@ pub fn from_cmdline_args(args: &[String]) -> bool { } }; + let render_api = match opt_match.opt_str("r").unwrap_or("gl".to_string()).as_slice() { + "mesa" => Mesa, + "gl" => OpenGL, + _ => { + args_fail("Unknown render api specified"); + return false; + } + }; + let opts = Opts { urls: urls, n_render_threads: n_render_threads, @@ -317,6 +336,7 @@ 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, }; set_opts(opts); diff --git a/ports/android/glut_app/Cargo.lock b/ports/android/glut_app/Cargo.lock index f75d9ca4776..4fbe533bd7d 100644 --- a/ports/android/glut_app/Cargo.lock +++ b/ports/android/glut_app/Cargo.lock @@ -225,6 +225,7 @@ dependencies = [ "net 0.0.1", "plugins 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "script_traits 0.0.1", "stb_image 0.1.0 (git+https://github.com/servo/rust-stb-image)", "style 0.0.1", "url 0.1.0 (git+https://github.com/servo/rust-url)", @@ -335,7 +336,7 @@ source = "git+https://github.com/bjz/gl-rs.git#79cd3b3f9f19aa0e39f6af572fc8673a6 [[package]] name = "layers" version = "0.1.0" -source = "git+https://github.com/servo/rust-layers#84f6dd677aa681b03cfcbf89000d2c7b12684bd3" +source = "git+https://github.com/servo/rust-layers#0f6edd58b3b572f2aac97567b752c15db5fa1982" dependencies = [ "cgl 0.0.1 (git+https://github.com/servo/rust-cgl)", "core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)", diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index e9915dd380c..2e2a3d3f161 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -235,6 +235,7 @@ dependencies = [ "net 0.0.1", "plugins 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", + "script_traits 0.0.1", "stb_image 0.1.0 (git+https://github.com/servo/rust-stb-image)", "style 0.0.1", "url 0.1.0 (git+https://github.com/servo/rust-url)", @@ -286,6 +287,7 @@ dependencies = [ "cgl 0.0.1 (git+https://github.com/servo/rust-cgl)", "compositing 0.0.1", "geom 0.1.0 (git+https://github.com/servo/rust-geom)", + "gleam 0.0.1 (git+https://github.com/servo/gleam)", "glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo)", "layers 0.1.0 (git+https://github.com/servo/rust-layers)", "msg 0.0.1", @@ -365,7 +367,7 @@ source = "git+https://github.com/bjz/gl-rs.git#79cd3b3f9f19aa0e39f6af572fc8673a6 [[package]] name = "layers" version = "0.1.0" -source = "git+https://github.com/servo/rust-layers#84f6dd677aa681b03cfcbf89000d2c7b12684bd3" +source = "git+https://github.com/servo/rust-layers#0f6edd58b3b572f2aac97567b752c15db5fa1982" dependencies = [ "cgl 0.0.1 (git+https://github.com/servo/rust-cgl)", "core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)", diff --git a/ports/cef/core.rs b/ports/cef/core.rs index 16558b43bd8..69960b146bc 100644 --- a/ports/cef/core.rs +++ b/ports/cef/core.rs @@ -10,6 +10,7 @@ use libc::{c_int, c_void}; use native; use servo::Browser; use servo_util::opts; +use servo_util::opts::OpenGL; use types::{cef_app_t, cef_main_args_t, cef_settings_t}; #[no_mangle] @@ -68,6 +69,7 @@ pub extern "C" fn cef_run_message_loop() { user_agent: None, dump_flow_tree: false, validate_display_list_geometry: false, + render_api: OpenGL, }); native::start(0, 0 as *const *const u8, proc() { let window = glfw_app::create_window(); diff --git a/ports/glfw/Cargo.toml b/ports/glfw/Cargo.toml index 0b66b7e9cb0..5c32ba229e8 100644 --- a/ports/glfw/Cargo.toml +++ b/ports/glfw/Cargo.toml @@ -31,3 +31,6 @@ path = "../../components/util" [dependencies.cgl] git = "https://github.com/servo/rust-cgl" + +[dependencies.gleam] +git = "https://github.com/servo/gleam" diff --git a/ports/glfw/window.rs b/ports/glfw/window.rs index 3bf18949280..b24eef7754a 100644 --- a/ports/glfw/window.rs +++ b/ports/glfw/window.rs @@ -301,21 +301,21 @@ impl Window { match self.ready_state.get() { Blank => { - self.glfw_window.set_title("blank — Servo") + self.glfw_window.set_title("blank — Servo [GLFW]") } Loading => { - self.glfw_window.set_title("Loading — Servo") + self.glfw_window.set_title("Loading — Servo [GLFW]") } PerformingLayout => { - self.glfw_window.set_title("Performing Layout — Servo") + self.glfw_window.set_title("Performing Layout — Servo [GLFW]") } FinishedLoading => { match self.render_state.get() { RenderingRenderState => { - self.glfw_window.set_title("Rendering — Servo") + self.glfw_window.set_title("Rendering — Servo [GLFW]") } IdleRenderState => { - self.glfw_window.set_title("Servo") + self.glfw_window.set_title("Servo [GLFW]") } } } diff --git a/ports/glutin/Cargo.toml b/ports/glutin/Cargo.toml new file mode 100644 index 00000000000..12698526eb9 --- /dev/null +++ b/ports/glutin/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "glutin_app" +version = "0.0.1" +authors = ["The Servo Project Developers"] + +[lib] +name = "glutin_app" +path = "lib.rs" + +[dependencies.alert] +git = "https://github.com/servo/rust-alert" + +[dependencies.compositing] +path = "../../components/compositing" + +[dependencies.geom] +git = "https://github.com/servo/rust-geom" + +[dependencies.layers] +git = "https://github.com/servo/rust-layers" + +[dependencies.msg] +path = "../../components/msg" + +[dependencies.util] +path = "../../components/util" + +[dependencies.glutin] +git = "https://github.com/tomaka/glutin" +features = ["window", "headless"] + +[dependencies.gleam] +git = "https://github.com/servo/gleam" + +[dependencies.cgl] +git = "https://github.com/servo/rust-cgl" diff --git a/ports/glutin/lib.rs b/ports/glutin/lib.rs new file mode 100644 index 00000000000..8fc28a8debc --- /dev/null +++ b/ports/glutin/lib.rs @@ -0,0 +1,46 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +//! A simple application that uses GLFW to open a window for Servo to display in. + +#![license = "MPL"] +#![feature(macro_rules)] +#![deny(unused_imports, unused_variable)] + +extern crate alert; +#[cfg(target_os="macos")] +extern crate cgl; +extern crate compositing; +extern crate geom; +extern crate gleam; +extern crate glutin; +extern crate layers; +extern crate libc; +extern crate msg; +extern crate time; +extern crate util; +extern crate egl; + +use compositing::windowing::WindowEvent; +use geom::scale_factor::ScaleFactor; +use std::rc::Rc; +use window::Window; +use util::opts; + +pub mod window; + +pub trait NestedEventLoopListener { + fn handle_event_from_nested_event_loop(&mut self, event: WindowEvent) -> bool; +} + +pub fn create_window() -> Rc<Window> { + // Read command-line options. + let opts = opts::get(); + let foreground = opts.output_file.is_none(); + let scale_factor = opts.device_pixels_per_px.unwrap_or(ScaleFactor(1.0)); + let size = opts.initial_window_size.as_f32() * scale_factor; + + // Open a window. + Window::new(foreground, size.as_uint(), opts.render_api) +} diff --git a/ports/glutin/window.rs b/ports/glutin/window.rs new file mode 100644 index 00000000000..bb58b76587c --- /dev/null +++ b/ports/glutin/window.rs @@ -0,0 +1,479 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +//! A windowing implementation using GLFW. + +use alert::{Alert, AlertMethods}; +use compositing::compositor_task::{mod, CompositorProxy, CompositorReceiver}; +use compositing::windowing::{WindowEvent, WindowMethods}; +use compositing::windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent}; +use compositing::windowing::{MouseWindowEventClass, MouseWindowMoveEventClass, ScrollWindowEvent}; +use compositing::windowing::{ZoomWindowEvent, PinchZoomWindowEvent, NavigationWindowEvent}; +use compositing::windowing::{FinishedWindowEvent, QuitWindowEvent, MouseWindowClickEvent}; +use compositing::windowing::{MouseWindowMouseDownEvent, MouseWindowMouseUpEvent}; +use compositing::windowing::{Forward, Back}; +use geom::point::{Point2D, TypedPoint2D}; +use geom::scale_factor::ScaleFactor; +use geom::size::TypedSize2D; +use gleam::gl; +use layers::geometry::DevicePixel; +use layers::platform::surface::NativeGraphicsMetadata; +use msg::compositor_msg::{IdleRenderState, RenderState, RenderingRenderState}; +use msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayout, ReadyState}; +use std::cell::{Cell, RefCell}; +use std::rc::Rc; +use time::{mod, Timespec}; +use util::geometry::ScreenPx; +use util::opts::{RenderApi, Mesa, OpenGL}; +use glutin; +use NestedEventLoopListener; + +#[cfg(target_os="linux")] +use std::ptr; + +struct HeadlessContext { + // Although currently unused, this context needs to be stored. + // Otherwise, its drop() is called, deleting the mesa context + // before it can be used. + _context: glutin::HeadlessContext, + size: TypedSize2D<DevicePixel, uint>, +} + +enum WindowHandle { + Windowed(glutin::Window), + Headless(HeadlessContext), +} + +bitflags!( + #[deriving(Show)] + flags KeyModifiers: u8 { + const LEFT_CONTROL = 1, + const RIGHT_CONTROL = 2, + const LEFT_SHIFT = 4, + const RIGHT_SHIFT = 8, + const LEFT_ALT = 16, + const RIGHT_ALT = 32, + } +) + +/// The type of a window. +pub struct Window { + glutin: WindowHandle, + + mouse_down_button: Cell<Option<glutin::MouseButton>>, + mouse_down_point: Cell<Point2D<int>>, + event_queue: RefCell<Vec<WindowEvent>>, + + mouse_pos: Cell<Point2D<int>>, + ready_state: Cell<ReadyState>, + render_state: Cell<RenderState>, + key_modifiers: Cell<KeyModifiers>, + + last_title_set_time: Cell<Timespec>, +} + +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 { + OpenGL => { + let glutin_window = glutin::WindowBuilder::new() + .with_title("Servo [glutin]".to_string()) + .with_dimensions(window_size.width, window_size.height) + .with_gl_version((3, 0)) + .with_visibility(is_foreground) + .build() + .unwrap(); + unsafe { glutin_window.make_current() }; + + gl::load_with(|s| glutin_window.get_proc_address(s)); + + Windowed(glutin_window) + } + 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() }; + + gl::load_with(|s| headless_context.get_proc_address(s)); + + Headless(HeadlessContext { + _context: headless_context, + size: size, + }) + } + }; + + let window = Window { + glutin: glutin, + event_queue: RefCell::new(vec!()), + mouse_down_button: Cell::new(None), + mouse_down_point: Cell::new(Point2D(0, 0)), + + mouse_pos: Cell::new(Point2D(0, 0)), + ready_state: Cell::new(Blank), + render_state: Cell::new(IdleRenderState), + key_modifiers: Cell::new(KeyModifiers::empty()), + + last_title_set_time: Cell::new(Timespec::new(0, 0)), + }; + + 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 { + Windowed(ref window) => window.get_inner_size(), + 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 { + Windowed(ref window) => window.get_inner_size(), + 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 { + Windowed(ref window) => window.swap_buffers(), + Headless(_) => {}, + } + } + + fn create_compositor_channel(_: &Option<Rc<Window>>) + -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) { + let (sender, receiver) = channel(); + (box GlutinCompositorProxy { + sender: sender, + } 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 render state. + fn set_render_state(&self, render_state: RenderState) { + if self.ready_state.get() == FinishedLoading && + self.render_state.get() == RenderingRenderState && + render_state == IdleRenderState { + // page loaded + self.event_queue.borrow_mut().push(FinishedWindowEvent); + } + + self.render_state.set(render_state); + self.update_window_title() + } + + fn hidpi_factor(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> { + // TODO - handle hidpi + ScaleFactor(1.0) + } + + #[cfg(target_os="linux")] + fn native_metadata(&self) -> NativeGraphicsMetadata { + match self.glutin { + Windowed(ref window) => { + NativeGraphicsMetadata { + display: unsafe { window.platform_display() } + } + } + 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()), + } + } + } +} + +impl Window { + /// Helper function to set the window title in accordance with the ready state. + fn update_window_title(&self) { + match self.glutin { + 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() { + Blank => { + window.set_title("blank - Servo [glutin]") + } + Loading => { + window.set_title("Loading - Servo [glutin]") + } + PerformingLayout => { + window.set_title("Performing Layout - Servo [glutin]") + } + FinishedLoading => { + match self.render_state.get() { + RenderingRenderState => { + window.set_title("Rendering - Servo [glutin]") + } + IdleRenderState => { + window.set_title("Servo [glutin]") + } + } + } + } + } + Headless(_) => {}, + } + } +} + +impl Window { + fn handle_window_event(&self, event: glutin::Event) -> bool { + match event { + glutin::KeyboardInput(element_state, _scan_code, virtual_key_code) => { + if virtual_key_code.is_some() { + let virtual_key_code = virtual_key_code.unwrap(); + + match (element_state, virtual_key_code) { + (_, glutin::LControl) => self.toggle_modifier(LEFT_CONTROL), + (_, glutin::RControl) => self.toggle_modifier(RIGHT_CONTROL), + (_, glutin::LShift) => self.toggle_modifier(LEFT_SHIFT), + (_, glutin::RShift) => self.toggle_modifier(RIGHT_SHIFT), + (_, glutin::LAlt) => self.toggle_modifier(LEFT_ALT), + (_, glutin::RAlt) => self.toggle_modifier(RIGHT_ALT), + (glutin::Pressed, key_code) => return self.handle_key(key_code), + (_, _) => {} + } + } + } + glutin::Resized(width, height) => { + self.event_queue.borrow_mut().push(ResizeWindowEvent(TypedSize2D(width, height))); + } + glutin::MouseInput(element_state, mouse_button) => { + if mouse_button == glutin::LeftMouseButton || + mouse_button == glutin::RightMouseButton { + let mouse_pos = self.mouse_pos.get(); + self.handle_mouse(mouse_button, element_state, mouse_pos.x, mouse_pos.y); + } + } + glutin::MouseMoved((x, y)) => { + self.mouse_pos.set(Point2D(x, y)); + self.event_queue.borrow_mut().push( + MouseWindowMoveEventClass(TypedPoint2D(x as f32, y as f32))); + } + glutin::MouseWheel(delta) => { + if self.ctrl_pressed() { + // Ctrl-Scrollwheel simulates a "pinch zoom" gesture. + if delta < 0 { + self.event_queue.borrow_mut().push(PinchZoomWindowEvent(1.0/1.1)); + } else if delta > 0 { + self.event_queue.borrow_mut().push(PinchZoomWindowEvent(1.1)); + } + } else { + let dx = 0.0; + let dy = (delta as f32) * 30.0; + self.scroll_window(dx, dy); + } + }, + _ => {} + } + + false + } + + #[inline] + fn ctrl_pressed(&self) -> bool { + self.key_modifiers.get().intersects(LEFT_CONTROL | RIGHT_CONTROL) + } + + #[inline] + fn shift_pressed(&self) -> bool { + self.key_modifiers.get().intersects(LEFT_SHIFT | RIGHT_SHIFT) + } + + fn toggle_modifier(&self, modifier: KeyModifiers) { + let mut modifiers = self.key_modifiers.get(); + modifiers.toggle(modifier); + self.key_modifiers.set(modifiers); + } + + /// Helper function to send a scroll event. + fn scroll_window(&self, dx: f32, dy: f32) { + let mouse_pos = self.mouse_pos.get(); + let event = ScrollWindowEvent(TypedPoint2D(dx as f32, dy as f32), + TypedPoint2D(mouse_pos.x as i32, mouse_pos.y as i32)); + self.event_queue.borrow_mut().push(event); + } + + /// Helper function to handle keyboard events. + fn handle_key(&self, key: glutin::VirtualKeyCode) -> bool { + match key { + glutin::Escape => return true, + glutin::L if self.ctrl_pressed() => { + self.load_url(); // Ctrl+L + } + glutin::Equals if self.ctrl_pressed() => { // Ctrl-+ + self.event_queue.borrow_mut().push(ZoomWindowEvent(1.1)); + } + glutin::Minus if self.ctrl_pressed() => { // Ctrl-- + self.event_queue.borrow_mut().push(ZoomWindowEvent(1.0/1.1)); + } + glutin::Back if self.shift_pressed() => { // Shift-Backspace + self.event_queue.borrow_mut().push(NavigationWindowEvent(Forward)); + } + glutin::Back => { // Backspace + self.event_queue.borrow_mut().push(NavigationWindowEvent(Back)); + } + glutin::PageDown => { + self.scroll_window(0.0, -self.framebuffer_size().as_f32().to_untyped().height); + } + glutin::PageUp => { + self.scroll_window(0.0, self.framebuffer_size().as_f32().to_untyped().height); + } + _ => {} + } + + false + } + + /// Helper function to handle a click + fn handle_mouse(&self, button: glutin::MouseButton, action: glutin::ElementState, x: int, y: int) { + // FIXME(tkuehn): max pixel dist should be based on pixel density + let max_pixel_dist = 10f64; + let event = match action { + glutin::Pressed => { + self.mouse_down_point.set(Point2D(x, y)); + self.mouse_down_button.set(Some(button)); + MouseWindowMouseDownEvent(0, TypedPoint2D(x as f32, y as f32)) + } + glutin::Released => { + match self.mouse_down_button.get() { + None => (), + Some(but) if button == but => { + let pixel_dist = self.mouse_down_point.get() - Point2D(x, y); + let pixel_dist = ((pixel_dist.x * pixel_dist.x + + pixel_dist.y * pixel_dist.y) as f64).sqrt(); + if pixel_dist < max_pixel_dist { + let click_event = MouseWindowClickEvent(0, + TypedPoint2D(x as f32, + y as f32)); + self.event_queue.borrow_mut().push(MouseWindowEventClass(click_event)); + } + } + Some(_) => (), + } + MouseWindowMouseUpEvent(0, TypedPoint2D(x as f32, y as f32)) + } + }; + self.event_queue.borrow_mut().push(MouseWindowEventClass(event)); + } + + /// Helper function to pop up an alert box prompting the user to load a URL. + fn load_url(&self) { + let mut alert: Alert = AlertMethods::new("Navigate to:"); + alert.add_prompt(); + alert.run(); + let value = alert.prompt_value(); + if "" == value.as_slice() { // To avoid crashing on Linux. + self.event_queue.borrow_mut().push(LoadUrlWindowEvent("http://purple.com/".to_string())) + } else { + self.event_queue.borrow_mut().push(LoadUrlWindowEvent(value.clone())) + } + } + + pub unsafe fn set_nested_event_loop_listener( + &self, + _listener: *mut NestedEventLoopListener + 'static) { + // TODO: Support this with glutin + //self.glfw_window.set_refresh_polling(false); + //glfw::ffi::glfwSetWindowRefreshCallback(self.glfw_window.ptr, Some(on_refresh)); + //glfw::ffi::glfwSetFramebufferSizeCallback(self.glfw_window.ptr, Some(on_framebuffer_size)); + //g_nested_event_loop_listener = Some(listener) + } + + pub unsafe fn remove_nested_event_loop_listener(&self) { + // TODO: Support this with glutin + //glfw::ffi::glfwSetWindowRefreshCallback(self.glfw_window.ptr, None); + //glfw::ffi::glfwSetFramebufferSizeCallback(self.glfw_window.ptr, None); + //self.glfw_window.set_refresh_polling(true); + //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(); + } + } + + match self.glutin { + Windowed(ref window) => { + let mut close_event = false; + for event in window.poll_events() { + close_event = self.handle_window_event(event); + if close_event { + break; + } + } + + if close_event || window.is_closed() { + QuitWindowEvent + } else { + self.event_queue.borrow_mut().remove(0).unwrap_or(IdleWindowEvent) + } + } + Headless(_) => { + self.event_queue.borrow_mut().remove(0).unwrap_or(IdleWindowEvent) + } + } + } +} + +struct GlutinCompositorProxy { + sender: Sender<compositor_task::Msg>, +} + +impl CompositorProxy for GlutinCompositorProxy { + fn send(&mut self, msg: compositor_task::Msg) { + // Send a message and kick the OS event loop awake. + self.sender.send(msg); + // TODO: Support this with glutin + //glfw::Glfw::post_empty_event() + } + fn clone_compositor_proxy(&self) -> Box<CompositorProxy+Send> { + box GlutinCompositorProxy { + sender: self.sender.clone(), + } as Box<CompositorProxy+Send> + } +} diff --git a/src/main.rs b/src/main.rs index 34c4d195000..16ef1980668 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,8 +13,11 @@ extern crate native; extern crate time; extern crate "util" as servo_util; -#[cfg(not(any(test,target_os="android")))] -extern crate glfw_app; +#[cfg(all(feature = "glutin",not(any(test,target_os="android"))))] +extern crate "glutin_app" as app; +#[cfg(all(feature = "glfw_app",not(any(test,target_os="android"))))] +extern crate "glfw_app" as app; + #[cfg(not(any(test,target_os="android")))] extern crate compositing; @@ -34,7 +37,7 @@ use std::os; #[cfg(not(any(test,target_os="android")))] struct BrowserWrapper { - browser: Browser<glfw_app::window::Window>, + browser: Browser<app::window::Window>, } #[cfg(not(any(test,target_os="android")))] @@ -46,7 +49,7 @@ fn start(argc: int, argv: *const *const u8) -> int { let window = if opts::get().headless { None } else { - Some(glfw_app::create_window()) + Some(app::create_window()) }; let mut browser = BrowserWrapper { @@ -95,7 +98,7 @@ fn start(argc: int, argv: *const *const u8) -> int { } #[cfg(not(any(test,target_os="android")))] -impl glfw_app::NestedEventLoopListener for BrowserWrapper { +impl app::NestedEventLoopListener for BrowserWrapper { fn handle_event_from_nested_event_loop(&mut self, event: WindowEvent) -> bool { let is_resize = match event { ResizeWindowEvent(..) => true, |