зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #4028 - Add glutin port (supported on Linux only currently) (from glennw:glutin); r=larsbergstrom
Default build uses glfw, but glutin can be enabled via: ./mach cargo build --no-default-features --features=glutin Remaining work: * Mac * Android * hi-dpi * nested event loop This PR also enables true headless (without X) rendering on Linux by specifying the rendering API as Mesa. Source-Repo: https://github.com/servo/servo Source-Revision: f5c6146de0b3bfda97edff6662033f4a981df3f6
This commit is contained in:
Родитель
2c925704ee
Коммит
c04e3b2bc7
|
@ -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",
|
||||
|
@ -24,6 +25,14 @@ dependencies = [
|
|||
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -57,11 +66,21 @@ dependencies = [
|
|||
"gleam 0.0.1 (git+https://github.com/servo/gleam)",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -279,12 +298,41 @@ 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",
|
||||
"util 0.0.1",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
@ -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)",
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)",
|
||||
|
|
|
@ -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)",
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -31,3 +31,6 @@ path = "../../components/util"
|
|||
|
||||
[dependencies.cgl]
|
||||
git = "https://github.com/servo/rust-cgl"
|
||||
|
||||
[dependencies.gleam]
|
||||
git = "https://github.com/servo/gleam"
|
||||
|
|
|
@ -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]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
|
@ -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)
|
||||
}
|
|
@ -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>
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче