Merge pull request #173 from tomaka/change-builder-attribs

Finalize window building API
This commit is contained in:
tomaka 2014-12-31 07:49:44 +01:00
commit 4d5e39f436
8 changed files with 131 additions and 61 deletions

View file

@ -7,8 +7,7 @@ use events::ElementState::{Pressed, Released};
use events::Event::{MouseInput, MouseMoved}; use events::Event::{MouseInput, MouseMoved};
use events::MouseButton::LeftMouseButton; use events::MouseButton::LeftMouseButton;
#[cfg(feature = "headless")] use BuilderAttribs;
use HeadlessRendererBuilder;
pub struct Window { pub struct Window {
display: ffi::egl::types::EGLDisplay, display: ffi::egl::types::EGLDisplay,
@ -45,7 +44,7 @@ pub struct HeadlessContext(int);
#[cfg(feature = "headless")] #[cfg(feature = "headless")]
impl HeadlessContext { impl HeadlessContext {
/// See the docs in the crate root file. /// See the docs in the crate root file.
pub fn new(_builder: HeadlessRendererBuilder) -> Result<HeadlessContext, CreationError> { pub fn new(_builder: BuilderAttribs) -> Result<HeadlessContext, CreationError> {
unimplemented!() unimplemented!()
} }
@ -66,7 +65,7 @@ unsafe impl Send for HeadlessContext {}
unsafe impl Sync for HeadlessContext {} unsafe impl Sync for HeadlessContext {}
impl Window { impl Window {
pub fn new(builder: WindowBuilder) -> Result<Window, CreationError> { pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> {
use std::{mem, ptr}; use std::{mem, ptr};
if builder.sharing.is_some() { if builder.sharing.is_some() {

View file

@ -70,12 +70,14 @@ pub struct MonitorID(winimpl::MonitorID);
#[deriving(Clone, Show, PartialEq, Eq)] #[deriving(Clone, Show, PartialEq, Eq)]
pub enum CreationError { pub enum CreationError {
OsError(String), OsError(String),
NotSupported,
} }
impl std::error::Error for CreationError { impl std::error::Error for CreationError {
fn description(&self) -> &str { fn description(&self) -> &str {
match self { match self {
&CreationError::OsError(ref text) => text.as_slice(), &CreationError::OsError(ref text) => text.as_slice(),
&CreationError::NotSupported => "Some of the requested attributes are not supported",
} }
} }
} }
@ -92,7 +94,14 @@ pub enum Api {
/// Object that allows you to build windows. /// Object that allows you to build windows.
#[cfg(feature = "window")] #[cfg(feature = "window")]
pub struct WindowBuilder<'a> { pub struct WindowBuilder<'a> {
sharing: Option<&'a Window>, attribs: BuilderAttribs<'a>
}
/// Attributes
struct BuilderAttribs<'a> {
headless: bool,
strict: bool,
sharing: Option<&'a winimpl::Window>,
dimensions: Option<(uint, uint)>, dimensions: Option<(uint, uint)>,
title: String, title: String,
monitor: Option<winimpl::MonitorID>, monitor: Option<winimpl::MonitorID>,
@ -101,13 +110,18 @@ pub struct WindowBuilder<'a> {
vsync: bool, vsync: bool,
visible: bool, visible: bool,
multisampling: Option<u16>, multisampling: Option<u16>,
depth_bits: Option<u8>,
stencil_bits: Option<u8>,
color_bits: Option<u8>,
alpha_bits: Option<u8>,
stereoscopy: bool,
} }
#[cfg(feature = "window")] impl BuilderAttribs<'static> {
impl<'a> WindowBuilder<'a> { fn new() -> BuilderAttribs<'static> {
/// Initializes a new `WindowBuilder` with default values. BuilderAttribs {
pub fn new() -> WindowBuilder<'a> { headless: false,
WindowBuilder { strict: false,
sharing: None, sharing: None,
dimensions: None, dimensions: None,
title: "glutin window".to_string(), title: "glutin window".to_string(),
@ -117,6 +131,21 @@ impl<'a> WindowBuilder<'a> {
vsync: false, vsync: false,
visible: true, visible: true,
multisampling: None, multisampling: None,
depth_bits: None,
stencil_bits: None,
color_bits: None,
alpha_bits: None,
stereoscopy: false,
}
}
}
#[cfg(feature = "window")]
impl<'a> WindowBuilder<'a> {
/// Initializes a new `WindowBuilder` with default values.
pub fn new() -> WindowBuilder<'a> {
WindowBuilder {
attribs: BuilderAttribs::new(),
} }
} }
@ -124,13 +153,13 @@ impl<'a> WindowBuilder<'a> {
/// ///
/// Width and height are in pixels. /// Width and height are in pixels.
pub fn with_dimensions(mut self, width: uint, height: uint) -> WindowBuilder<'a> { pub fn with_dimensions(mut self, width: uint, height: uint) -> WindowBuilder<'a> {
self.dimensions = Some((width, height)); self.attribs.dimensions = Some((width, height));
self self
} }
/// Requests a specific title for the window. /// Requests a specific title for the window.
pub fn with_title(mut self, title: String) -> WindowBuilder<'a> { pub fn with_title(mut self, title: String) -> WindowBuilder<'a> {
self.title = title; self.attribs.title = title;
self self
} }
@ -139,7 +168,7 @@ impl<'a> WindowBuilder<'a> {
/// If you don't specify dimensions for the window, it will match the monitor's. /// If you don't specify dimensions for the window, it will match the monitor's.
pub fn with_fullscreen(mut self, monitor: MonitorID) -> WindowBuilder<'a> { pub fn with_fullscreen(mut self, monitor: MonitorID) -> WindowBuilder<'a> {
let MonitorID(monitor) = monitor; let MonitorID(monitor) = monitor;
self.monitor = Some(monitor); self.attribs.monitor = Some(monitor);
self self
} }
@ -147,7 +176,7 @@ impl<'a> WindowBuilder<'a> {
/// ///
/// There are some exceptions, like FBOs or VAOs. See the OpenGL documentation. /// There are some exceptions, like FBOs or VAOs. See the OpenGL documentation.
pub fn with_shared_lists(mut self, other: &'a Window) -> WindowBuilder<'a> { pub fn with_shared_lists(mut self, other: &'a Window) -> WindowBuilder<'a> {
self.sharing = Some(other); self.attribs.sharing = Some(&other.window);
self self
} }
@ -156,7 +185,7 @@ impl<'a> WindowBuilder<'a> {
/// Version is a (major, minor) pair. For example to request OpenGL 3.3 /// Version is a (major, minor) pair. For example to request OpenGL 3.3
/// you would pass `(3, 3)`. /// you would pass `(3, 3)`.
pub fn with_gl_version(mut self, version: (uint, uint)) -> WindowBuilder<'a> { pub fn with_gl_version(mut self, version: (uint, uint)) -> WindowBuilder<'a> {
self.gl_version = Some(version); self.attribs.gl_version = Some(version);
self self
} }
@ -165,19 +194,19 @@ impl<'a> WindowBuilder<'a> {
/// The default value for this flag is `cfg!(ndebug)`, which means that it's enabled /// The default value for this flag is `cfg!(ndebug)`, which means that it's enabled
/// when you run `cargo build` and disabled when you run `cargo build --release`. /// when you run `cargo build` and disabled when you run `cargo build --release`.
pub fn with_gl_debug_flag(mut self, flag: bool) -> WindowBuilder<'a> { pub fn with_gl_debug_flag(mut self, flag: bool) -> WindowBuilder<'a> {
self.gl_debug = flag; self.attribs.gl_debug = flag;
self self
} }
/// Requests that the window has vsync enabled. /// Requests that the window has vsync enabled.
pub fn with_vsync(mut self) -> WindowBuilder<'a> { pub fn with_vsync(mut self) -> WindowBuilder<'a> {
self.vsync = true; self.attribs.vsync = true;
self self
} }
/// Sets whether the window will be initially hidden or visible. /// Sets whether the window will be initially hidden or visible.
pub fn with_visibility(mut self, visible: bool) -> WindowBuilder<'a> { pub fn with_visibility(mut self, visible: bool) -> WindowBuilder<'a> {
self.visible = visible; self.attribs.visible = visible;
self self
} }
@ -189,7 +218,32 @@ impl<'a> WindowBuilder<'a> {
pub fn with_multisampling(mut self, samples: u16) -> WindowBuilder<'a> { pub fn with_multisampling(mut self, samples: u16) -> WindowBuilder<'a> {
use std::num::UnsignedInt; use std::num::UnsignedInt;
assert!(samples.is_power_of_two()); assert!(samples.is_power_of_two());
self.multisampling = Some(samples); self.attribs.multisampling = Some(samples);
self
}
/// Sets the number of bits in the depth buffer.
pub fn with_depth_buffer(mut self, bits: u8) -> WindowBuilder<'a> {
self.attribs.depth_bits = Some(bits);
self
}
/// Sets the number of bits in the stencil buffer.
pub fn with_stencil_buffer(mut self, bits: u8) -> WindowBuilder<'a> {
self.attribs.stencil_bits = Some(bits);
self
}
/// Sets the number of bits in the color buffer.
pub fn with_pixel_format(mut self, color_bits: u8, alpha_bits: u8) -> WindowBuilder<'a> {
self.attribs.color_bits = Some(color_bits);
self.attribs.alpha_bits = Some(alpha_bits);
self
}
/// Request the backend to be stereoscopic.
pub fn with_stereoscopy(mut self) -> WindowBuilder<'a> {
self.attribs.stereoscopy = true;
self self
} }
@ -199,26 +253,33 @@ impl<'a> WindowBuilder<'a> {
/// out of memory, etc. /// out of memory, etc.
pub fn build(mut self) -> Result<Window, CreationError> { pub fn build(mut self) -> Result<Window, CreationError> {
// resizing the window to the dimensions of the monitor when fullscreen // resizing the window to the dimensions of the monitor when fullscreen
if self.dimensions.is_none() && self.monitor.is_some() { if self.attribs.dimensions.is_none() && self.attribs.monitor.is_some() {
self.dimensions = Some(self.monitor.as_ref().unwrap().get_dimensions()) self.attribs.dimensions = Some(self.attribs.monitor.as_ref().unwrap().get_dimensions())
} }
// default dimensions // default dimensions
if self.dimensions.is_none() { if self.attribs.dimensions.is_none() {
self.dimensions = Some((1024, 768)); self.attribs.dimensions = Some((1024, 768));
} }
// building // building
winimpl::Window::new(self).map(|w| Window { window: w }) winimpl::Window::new(self.attribs).map(|w| Window { window: w })
}
/// Builds the window.
///
/// The context is build in a *strict* way. That means that if the backend couldn't give
/// you what you requested, an `Err` will be returned.
pub fn build_strict(mut self) -> Result<Window, CreationError> {
self.attribs.strict = true;
self.build()
} }
} }
/// Object that allows you to build headless contexts. /// Object that allows you to build headless contexts.
#[cfg(feature = "headless")] #[cfg(feature = "headless")]
pub struct HeadlessRendererBuilder { pub struct HeadlessRendererBuilder {
dimensions: (uint, uint), attribs: BuilderAttribs<'static>,
gl_version: Option<(uint, uint)>,
gl_debug: bool,
} }
#[cfg(feature = "headless")] #[cfg(feature = "headless")]
@ -226,9 +287,11 @@ impl HeadlessRendererBuilder {
/// Initializes a new `HeadlessRendererBuilder` with default values. /// Initializes a new `HeadlessRendererBuilder` with default values.
pub fn new(width: uint, height: uint) -> HeadlessRendererBuilder { pub fn new(width: uint, height: uint) -> HeadlessRendererBuilder {
HeadlessRendererBuilder { HeadlessRendererBuilder {
dimensions: (width, height), attribs: BuilderAttribs {
gl_version: None, headless: true,
gl_debug: cfg!(ndebug), dimensions: Some((width, height)),
.. BuilderAttribs::new()
},
} }
} }
@ -237,7 +300,7 @@ impl HeadlessRendererBuilder {
/// Version is a (major, minor) pair. For example to request OpenGL 3.3 /// Version is a (major, minor) pair. For example to request OpenGL 3.3
/// you would pass `(3, 3)`. /// you would pass `(3, 3)`.
pub fn with_gl_version(mut self, version: (uint, uint)) -> HeadlessRendererBuilder { pub fn with_gl_version(mut self, version: (uint, uint)) -> HeadlessRendererBuilder {
self.gl_version = Some(version); self.attribs.gl_version = Some(version);
self self
} }
@ -246,7 +309,7 @@ impl HeadlessRendererBuilder {
/// The default value for this flag is `cfg!(ndebug)`, which means that it's enabled /// The default value for this flag is `cfg!(ndebug)`, which means that it's enabled
/// when you run `cargo build` and disabled when you run `cargo build --release`. /// when you run `cargo build` and disabled when you run `cargo build --release`.
pub fn with_gl_debug_flag(mut self, flag: bool) -> HeadlessRendererBuilder { pub fn with_gl_debug_flag(mut self, flag: bool) -> HeadlessRendererBuilder {
self.gl_debug = flag; self.attribs.gl_debug = flag;
self self
} }
@ -255,7 +318,16 @@ impl HeadlessRendererBuilder {
/// Error should be very rare and only occur in case of permission denied, incompatible system, /// Error should be very rare and only occur in case of permission denied, incompatible system,
/// out of memory, etc. /// out of memory, etc.
pub fn build(self) -> Result<HeadlessContext, CreationError> { pub fn build(self) -> Result<HeadlessContext, CreationError> {
winimpl::HeadlessContext::new(self).map(|w| HeadlessContext { context: w }) winimpl::HeadlessContext::new(self.attribs).map(|w| HeadlessContext { context: w })
}
/// Builds the headless context.
///
/// The context is build in a *strict* way. That means that if the backend couldn't give
/// you what you requested, an `Err` will be returned.
pub fn build_strict(mut self) -> Result<HeadlessContext, CreationError> {
self.attribs.strict = true;
self.build()
} }
} }

View file

@ -1,6 +1,6 @@
use CreationError; use CreationError;
use CreationError::OsError; use CreationError::OsError;
use HeadlessRendererBuilder; use BuilderAttribs;
use libc; use libc;
use std::ptr; use std::ptr;
@ -30,7 +30,7 @@ pub struct HeadlessContext {
} }
impl HeadlessContext { impl HeadlessContext {
pub fn new(builder: HeadlessRendererBuilder) -> Result<HeadlessContext, CreationError> { pub fn new(builder: BuilderAttribs) -> Result<HeadlessContext, CreationError> {
let (width, height) = builder.dimensions; let (width, height) = builder.dimensions;
let context = unsafe { let context = unsafe {
let attributes = [ let attributes = [

View file

@ -5,8 +5,7 @@ use {CreationError, Event};
use CreationError::OsError; use CreationError::OsError;
use libc; use libc;
#[cfg(feature = "window")] use BuilderAttribs;
use WindowBuilder;
use cocoa::base::{id, NSUInteger, nil, objc_allocateClassPair, class, objc_registerClassPair}; use cocoa::base::{id, NSUInteger, nil, objc_allocateClassPair, class, objc_registerClassPair};
use cocoa::base::{selector, msg_send, class_addMethod, class_addIvar}; use cocoa::base::{selector, msg_send, class_addMethod, class_addIvar};
@ -63,7 +62,7 @@ pub struct Window {
#[cfg(feature = "window")] #[cfg(feature = "window")]
impl Window { impl Window {
pub fn new(builder: WindowBuilder) -> Result<Window, CreationError> { pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> {
if builder.sharing.is_some() { if builder.sharing.is_some() {
unimplemented!() unimplemented!()
} }

View file

@ -3,11 +3,7 @@ use std::ptr;
use libc; use libc;
use {CreationError, Event}; use {CreationError, Event};
#[cfg(feature = "window")] use BuilderAttribs;
use WindowBuilder;
#[cfg(feature = "headless")]
use HeadlessRendererBuilder;
pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor}; pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor};
@ -19,15 +15,13 @@ mod init;
mod monitor; mod monitor;
/// ///
#[cfg(feature = "headless")]
pub struct HeadlessContext(Window); pub struct HeadlessContext(Window);
#[cfg(feature = "headless")]
impl HeadlessContext { impl HeadlessContext {
/// See the docs in the crate root file. /// See the docs in the crate root file.
pub fn new(builder: HeadlessRendererBuilder) -> Result<HeadlessContext, CreationError> { pub fn new(builder: BuilderAttribs) -> Result<HeadlessContext, CreationError> {
let HeadlessRendererBuilder { dimensions, gl_version, gl_debug } = builder; let BuilderAttribs { dimensions, gl_version, gl_debug, .. } = builder;
init::new_window(Some(dimensions), "".to_string(), None, gl_version, gl_debug, false, true, init::new_window(dimensions, "".to_string(), None, gl_version, gl_debug, false, true,
None, None) None, None)
.map(|w| HeadlessContext(w)) .map(|w| HeadlessContext(w))
} }
@ -83,14 +77,13 @@ pub struct Window {
unsafe impl Send for Window {} unsafe impl Send for Window {}
unsafe impl Sync for Window {} unsafe impl Sync for Window {}
#[cfg(feature = "window")]
impl Window { impl Window {
/// See the docs in the crate root file. /// See the docs in the crate root file.
pub fn new(builder: WindowBuilder) -> Result<Window, CreationError> { pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> {
let WindowBuilder { dimensions, title, monitor, gl_version, let BuilderAttribs { dimensions, title, monitor, gl_version,
gl_debug, vsync, visible, sharing, multisampling } = builder; gl_debug, vsync, visible, sharing, multisampling, .. } = builder;
init::new_window(dimensions, title, monitor, gl_version, gl_debug, vsync, init::new_window(dimensions, title, monitor, gl_version, gl_debug, vsync,
!visible, sharing.map(|w| init::ContextHack(w.window.context)), !visible, sharing.map(|w| init::ContextHack(w.context)),
multisampling) multisampling)
} }
} }

View file

@ -1,4 +1,4 @@
use HeadlessRendererBuilder; use BuilderAttribs;
use CreationError; use CreationError;
use CreationError::OsError; use CreationError::OsError;
use libc; use libc;
@ -13,11 +13,13 @@ pub struct HeadlessContext {
} }
impl HeadlessContext { impl HeadlessContext {
pub fn new(builder: HeadlessRendererBuilder) -> Result<HeadlessContext, CreationError> { pub fn new(builder: BuilderAttribs) -> Result<HeadlessContext, CreationError> {
let dimensions = builder.dimensions.unwrap();
Ok(HeadlessContext { Ok(HeadlessContext {
width: builder.dimensions.0, width: dimensions.0,
height: builder.dimensions.1, height: dimensions.1,
buffer: Vec::from_elem(builder.dimensions.0 * builder.dimensions.1, unsafe { mem::uninitialized() }), buffer: Vec::from_elem(dimensions.0 * dimensions.1, unsafe { mem::uninitialized() }),
context: unsafe { context: unsafe {
let ctxt = ffi::OSMesaCreateContext(0x1908, ptr::null()); let ctxt = ffi::OSMesaCreateContext(0x1908, ptr::null());
if ctxt.is_null() { if ctxt.is_null() {

View file

@ -11,3 +11,8 @@ mod headless;
#[cfg(feature = "window")] #[cfg(feature = "window")]
mod window; mod window;
#[cfg(not(feature = "window"))]
pub type Window = (); // TODO: hack to make things work
#[cfg(not(feature = "window"))]
pub type MonitorID = (); // TODO: hack to make things work

View file

@ -1,4 +1,4 @@
use {Event, WindowBuilder}; use {Event, BuilderAttribs};
use CreationError; use CreationError;
use CreationError::OsError; use CreationError::OsError;
use libc; use libc;
@ -89,7 +89,7 @@ pub struct Window {
} }
impl Window { impl Window {
pub fn new(builder: WindowBuilder) -> Result<Window, CreationError> { pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> {
ensure_thread_init(); ensure_thread_init();
let dimensions = builder.dimensions.unwrap_or((800, 600)); let dimensions = builder.dimensions.unwrap_or((800, 600));
@ -308,7 +308,7 @@ impl Window {
}); });
let share = if let Some(win) = builder.sharing { let share = if let Some(win) = builder.sharing {
win.window.x.context win.x.context
} else { } else {
ptr::null() ptr::null()
}; };