aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGlenn Watson <gw@intuitionlibrary.com>2014-11-18 11:10:02 +1000
committerGlenn Watson <gw@intuitionlibrary.com>2014-11-19 08:31:34 +1000
commit0278920343bea47d9ba736830fa2032c483363a3 (patch)
tree4d105154bbd1c78f940e3fc143add134c83c8343
parent64cc9ec6881add395b93341a32e90546b2f7211f (diff)
downloadservo-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.lock50
-rw-r--r--Cargo.toml5
-rw-r--r--components/util/opts.rs22
-rw-r--r--ports/android/glut_app/Cargo.lock3
-rw-r--r--ports/cef/Cargo.lock4
-rw-r--r--ports/cef/core.rs2
-rw-r--r--ports/glfw/Cargo.toml3
-rw-r--r--ports/glfw/window.rs10
-rw-r--r--ports/glutin/Cargo.toml36
-rw-r--r--ports/glutin/lib.rs46
-rw-r--r--ports/glutin/window.rs479
-rw-r--r--src/main.rs13
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,