Move windowing code out of the compositor

This is mainly just moving code around, in preparation for further changes to
the "windowing" API.
This commit is contained in:
Matt Brubeck 2014-09-29 14:22:02 -07:00
parent bfb81a5d10
commit 77d32ee447
23 changed files with 828 additions and 206 deletions

View file

@ -40,10 +40,6 @@ git = "https://github.com/servo/rust-azure"
[dependencies.geom]
git = "https://github.com/servo/rust-geom"
[dependencies.glfw]
git = "https://github.com/servo/glfw-rs"
branch = "servo"
[dependencies.layers]
git = "https://github.com/servo/rust-layers"
@ -61,7 +57,3 @@ git = "https://github.com/servo/rust-core-graphics"
[dependencies.core_text]
git = "https://github.com/servo/rust-core-text"
[dependencies.glut]
git = "https://github.com/servo/rust-glut"

View file

@ -11,7 +11,6 @@ use constellation::SendableFrameTree;
use events;
use events::ScrollPositionChanged;
use pipeline::CompositionPipeline;
use platform::{Application, Window};
use windowing;
use windowing::{FinishedWindowEvent, IdleWindowEvent, LoadUrlWindowEvent, MouseWindowClickEvent};
use windowing::{MouseWindowEvent, MouseWindowEventClass, MouseWindowMouseDownEvent};
@ -54,7 +53,7 @@ use time::precise_time_s;
use url::Url;
pub struct IOCompositor {
pub struct IOCompositor<Window: WindowMethods> {
/// The application window.
window: Rc<Window>,
@ -136,22 +135,13 @@ enum ShutdownState {
FinishedShuttingDown,
}
impl IOCompositor {
fn new(app: &Application,
opts: Opts,
port: Receiver<Msg>,
constellation_chan: ConstellationChan,
time_profiler_chan: TimeProfilerChan,
memory_profiler_chan: MemoryProfilerChan) -> IOCompositor {
let scale_factor = match opts.device_pixels_per_px {
Some(device_pixels_per_px) => device_pixels_per_px,
None => ScaleFactor(1.0),
};
let framebuffer_size = opts.initial_window_size.as_f32() * scale_factor;
let window: Rc<Window> = WindowMethods::new(app, opts.output_file.is_none(),
framebuffer_size.as_uint());
impl<Window: WindowMethods> IOCompositor<Window> {
fn new(window: Rc<Window>,
opts: Opts,
port: Receiver<Msg>,
constellation_chan: ConstellationChan,
time_profiler_chan: TimeProfilerChan,
memory_profiler_chan: MemoryProfilerChan) -> IOCompositor<Window> {
// Create an initial layer tree.
//
@ -192,13 +182,13 @@ impl IOCompositor {
}
}
pub fn create(app: &Application,
pub fn create(window: Rc<Window>,
opts: Opts,
port: Receiver<Msg>,
constellation_chan: ConstellationChan,
time_profiler_chan: TimeProfilerChan,
memory_profiler_chan: MemoryProfilerChan) {
let mut compositor = IOCompositor::new(app,
let mut compositor = IOCompositor::new(window,
opts,
port,
constellation_chan,

View file

@ -7,8 +7,7 @@ pub use windowing;
use compositor;
use headless;
pub use constellation::SendableFrameTree;
use windowing::{ApplicationMethods, WindowMethods};
use platform::Application;
use windowing::WindowMethods;
use azure::azure_hl::{SourceSurfaceMethods, Color};
use geom::point::Point2D;
@ -23,6 +22,7 @@ use servo_util::memory::MemoryProfilerChan;
use servo_util::opts::Opts;
use servo_util::time::TimeProfilerChan;
use std::comm::{channel, Sender, Receiver};
use std::rc::Rc;
use url::Url;
@ -183,28 +183,9 @@ pub enum Msg {
LoadComplete(PipelineId, Url),
}
pub enum CompositorMode {
Windowed(Application),
Headless
}
pub struct CompositorTask {
pub mode: CompositorMode,
}
pub struct CompositorTask;
impl CompositorTask {
fn new(is_headless: bool) -> CompositorTask {
let mode: CompositorMode = if is_headless {
Headless
} else {
Windowed(ApplicationMethods::new())
};
CompositorTask {
mode: mode
}
}
/// Creates a graphics context. Platform-specific.
///
/// FIXME(pcwalton): Probably could be less platform-specific, using the metadata abstraction.
@ -217,24 +198,24 @@ impl CompositorTask {
NativeCompositingGraphicsContext::new()
}
pub fn create(opts: Opts,
pub fn create<Window: WindowMethods>(
window: Option<Rc<Window>>,
opts: Opts,
port: Receiver<Msg>,
constellation_chan: ConstellationChan,
time_profiler_chan: TimeProfilerChan,
memory_profiler_chan: MemoryProfilerChan) {
let compositor = CompositorTask::new(opts.headless);
match compositor.mode {
Windowed(ref app) => {
compositor::IOCompositor::create(app,
match window {
Some(window) => {
compositor::IOCompositor::create(window,
opts,
port,
constellation_chan.clone(),
time_profiler_chan,
memory_profiler_chan)
}
Headless => {
None => {
headless::NullCompositor::create(port,
constellation_chan.clone(),
time_profiler_chan,

View file

@ -19,10 +19,6 @@ extern crate azure;
extern crate devtools_traits;
extern crate geom;
extern crate gfx;
#[cfg(not(target_os="android"))]
extern crate glfw;
#[cfg(target_os="android")]
extern crate glut;
extern crate layers;
extern crate layout_traits;
extern crate opengles;
@ -56,7 +52,4 @@ mod headless;
pub mod pipeline;
pub mod constellation;
mod windowing;
#[path="platform/mod.rs"]
pub mod platform;
pub mod windowing;

View file

@ -1,383 +0,0 @@
/* 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 windowing::{ApplicationMethods, WindowEvent, WindowMethods};
use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass, MouseWindowMoveEventClass};
use windowing::{ScrollWindowEvent, ZoomWindowEvent, PinchZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent};
use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
use windowing::RefreshWindowEvent;
use windowing::{Forward, Back};
use alert::{Alert, AlertMethods};
use libc::{exit, c_int};
use time;
use time::Timespec;
use std::cell::{Cell, RefCell};
use std::comm::Receiver;
use std::rc::Rc;
use geom::point::{Point2D, TypedPoint2D};
use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use layers::geometry::DevicePixel;
use servo_msg::compositor_msg::{IdleRenderState, RenderState, RenderingRenderState};
use servo_msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayout, ReadyState};
use servo_util::geometry::ScreenPx;
use glfw;
use glfw::Context;
/// A structure responsible for setting up and tearing down the entire windowing system.
pub struct Application {
pub glfw: glfw::Glfw,
}
impl ApplicationMethods for Application {
fn new() -> Application {
let app = glfw::init(glfw::LOG_ERRORS);
match app {
Err(_) => {
// handles things like inability to connect to X
// cannot simply fail, since the runtime isn't up yet (causes a nasty abort)
println!("GLFW initialization failed");
unsafe { exit(1); }
}
Ok(app) => {
Application { glfw: app }
}
}
}
}
macro_rules! glfw_callback(
(
$callback:path ($($arg:ident: $arg_ty:ty),*) $block:expr
) => ({
struct GlfwCallback;
impl $callback for GlfwCallback {
fn call(&self $(, $arg: $arg_ty)*) {
$block
}
}
~GlfwCallback
});
(
[$($state:ident: $state_ty:ty),*],
$callback:path ($($arg:ident: $arg_ty:ty),*) $block:expr
) => ({
struct GlfwCallback {
$($state: $state_ty,)*
}
impl $callback for GlfwCallback {
fn call(&self $(, $arg: $arg_ty)*) {
$block
}
}
~GlfwCallback {
$($state: $state,)*
}
});
)
/// The type of a window.
pub struct Window {
glfw: glfw::Glfw,
glfw_window: glfw::Window,
events: Receiver<(f64, glfw::WindowEvent)>,
event_queue: RefCell<Vec<WindowEvent>>,
mouse_down_button: Cell<Option<glfw::MouseButton>>,
mouse_down_point: Cell<Point2D<c_int>>,
ready_state: Cell<ReadyState>,
render_state: Cell<RenderState>,
last_title_set_time: Cell<Timespec>,
}
impl WindowMethods<Application> for Window {
/// Creates a new window.
fn new(app: &Application, is_foreground: bool, size: TypedSize2D<DevicePixel, uint>) -> Rc<Window> {
// Create the GLFW window.
let window_size = size.to_untyped();
app.glfw.window_hint(glfw::Visible(is_foreground));
let (glfw_window, events) = app.glfw.create_window(window_size.width as u32,
window_size.height as u32,
"Servo", glfw::Windowed)
.expect("Failed to create GLFW window");
glfw_window.make_current();
// Create our window object.
let window = Window {
glfw: app.glfw,
glfw_window: glfw_window,
events: events,
event_queue: RefCell::new(vec!()),
mouse_down_button: Cell::new(None),
mouse_down_point: Cell::new(Point2D(0 as c_int, 0)),
ready_state: Cell::new(Blank),
render_state: Cell::new(IdleRenderState),
last_title_set_time: Cell::new(Timespec::new(0, 0)),
};
// Register event handlers.
window.glfw_window.set_framebuffer_size_polling(true);
window.glfw_window.set_refresh_polling(true);
window.glfw_window.set_key_polling(true);
window.glfw_window.set_mouse_button_polling(true);
window.glfw_window.set_cursor_pos_polling(true);
window.glfw_window.set_scroll_polling(true);
let wrapped_window = Rc::new(window);
wrapped_window
}
/// Returns the size of the window in hardware pixels.
fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint> {
let (width, height) = self.glfw_window.get_framebuffer_size();
TypedSize2D(width as uint, height as uint)
}
/// Returns the size of the window in density-independent "px" units.
fn size(&self) -> TypedSize2D<ScreenPx, f32> {
let (width, height) = self.glfw_window.get_size();
TypedSize2D(width as f32, height as f32)
}
/// Presents the window to the screen (perhaps by page flipping).
fn present(&self) {
self.glfw_window.swap_buffers();
}
fn recv(&self) -> WindowEvent {
{
let mut event_queue = self.event_queue.borrow_mut();
if !event_queue.is_empty() {
return event_queue.remove(0).unwrap();
}
}
self.glfw.poll_events();
for (_, event) in glfw::flush_messages(&self.events) {
self.handle_window_event(&self.glfw_window, event);
}
if self.glfw_window.should_close() {
QuitWindowEvent
} else {
self.event_queue.borrow_mut().remove(0).unwrap_or(IdleWindowEvent)
}
}
/// 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> {
let backing_size = self.framebuffer_size().width.get();
let window_size = self.size().width.get();
ScaleFactor((backing_size as f32) / window_size)
}
}
impl Window {
fn handle_window_event(&self, window: &glfw::Window, event: glfw::WindowEvent) {
match event {
glfw::KeyEvent(key, _, action, mods) => {
if action == glfw::Press {
self.handle_key(key, mods)
}
},
glfw::FramebufferSizeEvent(width, height) => {
self.event_queue.borrow_mut().push(
ResizeWindowEvent(TypedSize2D(width as uint, height as uint)));
},
glfw::RefreshEvent => {
self.event_queue.borrow_mut().push(RefreshWindowEvent);
},
glfw::MouseButtonEvent(button, action, _mods) => {
let (x, y) = window.get_cursor_pos();
//handle hidpi displays, since GLFW returns non-hi-def coordinates.
let (backing_size, _) = window.get_framebuffer_size();
let (window_size, _) = window.get_size();
let hidpi = (backing_size as f32) / (window_size as f32);
let x = x as f32 * hidpi;
let y = y as f32 * hidpi;
if button == glfw::MouseButtonLeft || button == glfw::MouseButtonRight {
self.handle_mouse(button, action, x as i32, y as i32);
}
},
glfw::CursorPosEvent(xpos, ypos) => {
self.event_queue.borrow_mut().push(
MouseWindowMoveEventClass(TypedPoint2D(xpos as f32, ypos as f32)));
},
glfw::ScrollEvent(xpos, ypos) => {
match (window.get_key(glfw::KeyLeftControl),
window.get_key(glfw::KeyRightControl)) {
(glfw::Press, _) | (_, glfw::Press) => {
// Ctrl-Scrollwheel simulates a "pinch zoom" gesture.
if ypos < 0.0 {
self.event_queue.borrow_mut().push(PinchZoomWindowEvent(1.0/1.1));
} else if ypos > 0.0 {
self.event_queue.borrow_mut().push(PinchZoomWindowEvent(1.1));
}
},
_ => {
let dx = (xpos as f32) * 30.0;
let dy = (ypos as f32) * 30.0;
self.scroll_window(dx, dy);
}
}
},
_ => {}
}
}
/// Helper function to send a scroll event.
fn scroll_window(&self, dx: f32, dy: f32) {
let (x, y) = self.glfw_window.get_cursor_pos();
//handle hidpi displays, since GLFW returns non-hi-def coordinates.
let (backing_size, _) = self.glfw_window.get_framebuffer_size();
let (window_size, _) = self.glfw_window.get_size();
let hidpi = (backing_size as f32) / (window_size as f32);
let x = x as f32 * hidpi;
let y = y as f32 * hidpi;
self.event_queue.borrow_mut().push(ScrollWindowEvent(TypedPoint2D(dx, dy),
TypedPoint2D(x as i32, y as i32)));
}
/// Helper function to set the window title in accordance with the ready state.
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() {
Blank => {
self.glfw_window.set_title("blank — Servo")
}
Loading => {
self.glfw_window.set_title("Loading — Servo")
}
PerformingLayout => {
self.glfw_window.set_title("Performing Layout — Servo")
}
FinishedLoading => {
match self.render_state.get() {
RenderingRenderState => {
self.glfw_window.set_title("Rendering — Servo")
}
IdleRenderState => {
self.glfw_window.set_title("Servo")
}
}
}
}
}
/// Helper function to handle keyboard events.
fn handle_key(&self, key: glfw::Key, mods: glfw::Modifiers) {
match key {
glfw::KeyEscape => self.glfw_window.set_should_close(true),
glfw::KeyL if mods.contains(glfw::Control) => self.load_url(), // Ctrl+L
glfw::KeyEqual if mods.contains(glfw::Control) => { // Ctrl-+
self.event_queue.borrow_mut().push(ZoomWindowEvent(1.1));
}
glfw::KeyMinus if mods.contains(glfw::Control) => { // Ctrl--
self.event_queue.borrow_mut().push(ZoomWindowEvent(1.0/1.1));
}
glfw::KeyBackspace if mods.contains(glfw::Shift) => { // Shift-Backspace
self.event_queue.borrow_mut().push(NavigationWindowEvent(Forward));
}
glfw::KeyBackspace => { // Backspace
self.event_queue.borrow_mut().push(NavigationWindowEvent(Back));
}
glfw::KeyPageDown => {
let (_, height) = self.glfw_window.get_size();
self.scroll_window(0.0, -height as f32);
}
glfw::KeyPageUp => {
let (_, height) = self.glfw_window.get_size();
self.scroll_window(0.0, height as f32);
}
_ => {}
}
}
/// Helper function to handle a click
fn handle_mouse(&self, button: glfw::MouseButton, action: glfw::Action, x: c_int, y: c_int) {
// FIXME(tkuehn): max pixel dist should be based on pixel density
let max_pixel_dist = 10f64;
let event = match action {
glfw::Press => {
self.mouse_down_point.set(Point2D(x, y));
self.mouse_down_button.set(Some(button));
MouseWindowMouseDownEvent(button as uint, TypedPoint2D(x as f32, y as f32))
}
glfw::Release => {
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(button as uint,
TypedPoint2D(x as f32, y as f32));
self.event_queue.borrow_mut().push(MouseWindowEventClass(click_event));
}
}
Some(_) => (),
}
MouseWindowMouseUpEvent(button as uint, TypedPoint2D(x as f32, y as f32))
}
_ => fail!("I cannot recognize the type of mouse action that occured. :-(")
};
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()))
}
}
}

View file

@ -1,304 +0,0 @@
/* 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 GLUT.
use windowing::{ApplicationMethods, WindowEvent, WindowMethods};
use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass};
use windowing::{ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, FinishedWindowEvent};
use windowing::{MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
use windowing::{Forward, Back};
use alert::{Alert, AlertMethods};
use libc::{c_int, c_uchar};
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use geom::point::{Point2D, TypedPoint2D};
use geom::scale_factor::ScaleFactor;
use geom::size::TypedSize2D;
use layers::geometry::DevicePixel;
use servo_msg::compositor_msg::{IdleRenderState, RenderState, RenderingRenderState};
use servo_msg::compositor_msg::{FinishedLoading, Blank, ReadyState};
use servo_util::geometry::ScreenPx;
use glut::glut::{ACTIVE_SHIFT, DOUBLE, WindowHeight};
use glut::glut::WindowWidth;
use glut::glut;
// static THROBBER: [char, ..8] = [ '⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷' ];
/// A structure responsible for setting up and tearing down the entire windowing system.
pub struct Application;
impl ApplicationMethods for Application {
fn new() -> Application {
glut::init();
glut::init_display_mode(DOUBLE);
Application
}
}
impl Drop for Application {
fn drop(&mut self) {
drop_local_window();
}
}
/// The type of a window.
pub struct Window {
pub glut_window: glut::Window,
pub event_queue: RefCell<Vec<WindowEvent>>,
pub drag_origin: Point2D<c_int>,
pub mouse_down_button: Cell<c_int>,
pub mouse_down_point: Cell<Point2D<c_int>>,
pub ready_state: Cell<ReadyState>,
pub render_state: Cell<RenderState>,
pub throbber_frame: Cell<u8>,
}
impl WindowMethods<Application> for Window {
/// Creates a new window.
fn new(_: &Application, _: bool, size: TypedSize2D<DevicePixel, uint>) -> Rc<Window> {
// Create the GLUT window.
let window_size = size.to_untyped();
glut::init_window_size(window_size.width, window_size.height);
let glut_window = glut::create_window("Servo".to_string());
// Create our window object.
let window = Window {
glut_window: glut_window,
event_queue: RefCell::new(vec!()),
drag_origin: Point2D(0 as c_int, 0),
mouse_down_button: Cell::new(0),
mouse_down_point: Cell::new(Point2D(0 as c_int, 0)),
ready_state: Cell::new(Blank),
render_state: Cell::new(IdleRenderState),
throbber_frame: Cell::new(0),
};
// Register event handlers.
//Added dummy display callback to freeglut. According to freeglut ref, we should register some kind of display callback after freeglut 3.0.
struct DisplayCallbackState;
impl glut::DisplayCallback for DisplayCallbackState {
fn call(&self) {
debug!("GLUT display func registgered");
}
}
glut::display_func(box DisplayCallbackState);
struct ReshapeCallbackState;
impl glut::ReshapeCallback for ReshapeCallbackState {
fn call(&self, width: c_int, height: c_int) {
let tmp = local_window();
tmp.event_queue.borrow_mut().push(ResizeWindowEvent(TypedSize2D(width as uint, height as uint)))
}
}
glut::reshape_func(glut_window, box ReshapeCallbackState);
struct KeyboardCallbackState;
impl glut::KeyboardCallback for KeyboardCallbackState {
fn call(&self, key: c_uchar, _x: c_int, _y: c_int) {
let tmp = local_window();
tmp.handle_key(key)
}
}
glut::keyboard_func(box KeyboardCallbackState);
struct MouseCallbackState;
impl glut::MouseCallback for MouseCallbackState {
fn call(&self, button: c_int, state: c_int, x: c_int, y: c_int) {
if button < 3 {
let tmp = local_window();
tmp.handle_mouse(button, state, x, y);
} else {
match button {
3 => {
let tmp = local_window();
tmp.event_queue.borrow_mut().push(ScrollWindowEvent(
TypedPoint2D(0.0f32, 5.0f32),
TypedPoint2D(0i32, 5i32)));
},
4 => {
let tmp = local_window();
tmp.event_queue.borrow_mut().push(ScrollWindowEvent(
TypedPoint2D(0.0f32, -5.0f32),
TypedPoint2D(0i32, -5i32)));
},
_ => {}
}
}
}
}
glut::mouse_func(box MouseCallbackState);
let wrapped_window = Rc::new(window);
install_local_window(wrapped_window.clone());
wrapped_window
}
/// Returns the size of the window in hardware pixels.
fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint> {
TypedSize2D(glut::get(WindowWidth) as uint, glut::get(WindowHeight) as uint)
}
/// Returns the size of the window in density-independent "px" units.
fn size(&self) -> TypedSize2D<ScreenPx, f32> {
self.framebuffer_size().as_f32() / self.hidpi_factor()
}
/// Presents the window to the screen (perhaps by page flipping).
fn present(&self) {
glut::swap_buffers();
}
fn recv(&self) -> WindowEvent {
if !self.event_queue.borrow_mut().is_empty() {
return self.event_queue.borrow_mut().remove(0).unwrap();
}
glut::check_loop();
self.event_queue.borrow_mut().remove(0).unwrap_or(IdleWindowEvent)
}
/// Sets the ready state.
fn set_ready_state(&self, ready_state: ReadyState) {
self.ready_state.set(ready_state);
//FIXME: set_window_title causes crash with Android version of freeGLUT. Temporarily blocked.
//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);
//FIXME: set_window_title causes crash with Android version of freeGLUT. Temporarily blocked.
//self.update_window_title()
}
fn hidpi_factor(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
//FIXME: Do nothing in GLUT now.
ScaleFactor(1.0)
}
}
impl Window {
/// Helper function to set the window title in accordance with the ready state.
// fn update_window_title(&self) {
// let throbber = THROBBER[self.throbber_frame];
// match self.ready_state {
// Blank => {
// glut::set_window_title(self.glut_window, "Blank")
// }
// Loading => {
// glut::set_window_title(self.glut_window, format!("{:c} Loading . Servo", throbber))
// }
// PerformingLayout => {
// glut::set_window_title(self.glut_window, format!("{:c} Performing Layout . Servo", throbber))
// }
// FinishedLoading => {
// match self.render_state {
// RenderingRenderState => {
// glut::set_window_title(self.glut_window, format!("{:c} Rendering . Servo", throbber))
// }
// IdleRenderState => glut::set_window_title(self.glut_window, "Servo"),
// }
// }
// }
// }
/// Helper function to handle keyboard events.
fn handle_key(&self, key: u8) {
debug!("got key: {}", key);
let modifiers = glut::get_modifiers();
match key {
42 => self.load_url(),
43 => self.event_queue.borrow_mut().push(ZoomWindowEvent(1.1)),
45 => self.event_queue.borrow_mut().push(ZoomWindowEvent(0.909090909)),
56 => self.event_queue.borrow_mut().push(ScrollWindowEvent(TypedPoint2D(0.0f32, 5.0f32),
TypedPoint2D(0i32, 5i32))),
50 => self.event_queue.borrow_mut().push(ScrollWindowEvent(TypedPoint2D(0.0f32, -5.0f32),
TypedPoint2D(0i32, -5i32))),
127 => {
if (modifiers & ACTIVE_SHIFT) != 0 {
self.event_queue.borrow_mut().push(NavigationWindowEvent(Forward));
}
else {
self.event_queue.borrow_mut().push(NavigationWindowEvent(Back));
}
}
_ => {}
}
}
/// Helper function to handle a click
fn handle_mouse(&self, button: c_int, state: c_int, x: c_int, y: c_int) {
// FIXME(tkuehn): max pixel dist should be based on pixel density
let max_pixel_dist = 10f32;
let event = match state {
glut::MOUSE_DOWN => {
self.mouse_down_point.set(Point2D(x, y));
self.mouse_down_button.set(button);
MouseWindowMouseDownEvent(button as uint, TypedPoint2D(x as f32, y as f32))
}
glut::MOUSE_UP => {
if self.mouse_down_button.get() == button {
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 f32).sqrt();
if pixel_dist < max_pixel_dist {
let click_event = MouseWindowClickEvent(button as uint,
TypedPoint2D(x as f32, y as f32));
self.event_queue.borrow_mut().push(MouseWindowEventClass(click_event));
}
}
MouseWindowMouseUpEvent(button as uint, TypedPoint2D(x as f32, y as f32))
}
_ => fail!("I cannot recognize the type of mouse action that occured. :-(")
};
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()))
}
}
}
local_data_key!(TLS_KEY: Rc<Window>)
fn install_local_window(window: Rc<Window>) {
TLS_KEY.replace(Some(window));
}
fn drop_local_window() {
TLS_KEY.replace(None);
}
fn local_window() -> Rc<Window> {
TLS_KEY.get().unwrap().clone()
}

View file

@ -10,7 +10,6 @@ use geom::size::TypedSize2D;
use layers::geometry::DevicePixel;
use servo_msg::compositor_msg::{ReadyState, RenderState};
use servo_util::geometry::ScreenPx;
use std::rc::Rc;
pub enum MouseWindowEvent {
MouseWindowClickEvent(uint, TypedPoint2D<DevicePixel, f32>),
@ -54,14 +53,7 @@ pub enum WindowEvent {
QuitWindowEvent,
}
/// Methods for an abstract Application.
pub trait ApplicationMethods {
fn new() -> Self;
}
pub trait WindowMethods<A> {
/// Creates a new window.
fn new(app: &A, is_foreground: bool, size: TypedSize2D<DevicePixel, uint>) -> Rc<Self>;
pub trait WindowMethods {
/// Returns the size of the window in hardware pixels.
fn framebuffer_size(&self) -> TypedSize2D<DevicePixel, uint>;
/// Returns the size of the window in density-independent "px" units.