aboutsummaryrefslogtreecommitdiffstats
path: root/src/api/cocoa/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/cocoa/mod.rs')
-rw-r--r--src/api/cocoa/mod.rs251
1 files changed, 156 insertions, 95 deletions
diff --git a/src/api/cocoa/mod.rs b/src/api/cocoa/mod.rs
index 3254ee6..55ae617 100644
--- a/src/api/cocoa/mod.rs
+++ b/src/api/cocoa/mod.rs
@@ -1,6 +1,5 @@
#![cfg(target_os = "macos")]
-#[cfg(feature = "headless")]
pub use self::headless::HeadlessContext;
use {CreationError, Event, MouseCursor, CursorState};
@@ -9,6 +8,8 @@ use libc;
use Api;
use BuilderAttribs;
+use GlContext;
+use GlProfile;
use GlRequest;
use PixelFormat;
use native_monitor::NativeMonitorId;
@@ -27,7 +28,7 @@ use core_foundation::base::TCFType;
use core_foundation::string::CFString;
use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName};
-use core_graphics::display::{CGMainDisplayID, CGDisplayPixelsHigh};
+use core_graphics::display::{CGAssociateMouseAndMouseCursorPosition, CGMainDisplayID, CGDisplayPixelsHigh};
use std::ffi::CStr;
use std::collections::VecDeque;
@@ -46,8 +47,6 @@ pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor};
mod monitor;
mod event;
-
-#[cfg(feature = "headless")]
mod headless;
static mut shift_pressed: bool = false;
@@ -156,6 +155,7 @@ pub struct Window {
view: IdRef,
window: IdRef,
context: IdRef,
+ pixel_format: PixelFormat,
delegate: WindowDelegate,
}
@@ -326,9 +326,8 @@ impl Window {
Some(app) => app,
None => { return Err(OsError(format!("Couldn't create NSApplication"))); },
};
- let window = match Window::create_window(builder.dimensions.unwrap_or((800, 600)),
- &*builder.title,
- &builder.monitor)
+
+ let window = match Window::create_window(&builder)
{
Some(window) => window,
None => { return Err(OsError(format!("Couldn't create NSWindow"))); },
@@ -338,9 +337,11 @@ impl Window {
None => { return Err(OsError(format!("Couldn't create NSView"))); },
};
- let context = match Window::create_context(*view, &builder) {
- Some(context) => context,
- None => { return Err(OsError(format!("Couldn't create OpenGL context"))); },
+ // TODO: perhaps we should return error from create_context so we can
+ // determine the cause of failure and possibly recover?
+ let (context, pf) = match Window::create_context(*view, &builder) {
+ Ok((Some(context), Some(pf))) => (context, pf),
+ _ => { return Err(OsError(format!("Couldn't create OpenGL context"))); },
};
unsafe {
@@ -365,6 +366,7 @@ impl Window {
view: view,
window: window,
context: context,
+ pixel_format: pf,
delegate: WindowDelegate::new(ds),
};
@@ -384,9 +386,9 @@ impl Window {
}
}
- fn create_window(dimensions: (u32, u32), title: &str, monitor: &Option<MonitorID>) -> Option<IdRef> {
+ fn create_window(builder: &BuilderAttribs) -> Option<IdRef> {
unsafe {
- let screen = match *monitor {
+ let screen = match builder.monitor {
Some(ref monitor_id) => {
let native_id = match monitor_id.get_native_identifier() {
NativeMonitorId::Numeric(num) => num,
@@ -418,7 +420,7 @@ impl Window {
let frame = match screen {
Some(screen) => NSScreen::frame(screen),
None => {
- let (width, height) = dimensions;
+ let (width, height) = builder.dimensions.unwrap_or((800, 600));
NSRect::new(NSPoint::new(0., 0.), NSSize::new(width as f64, height as f64))
}
};
@@ -439,7 +441,7 @@ impl Window {
NO,
));
window.non_nil().map(|window| {
- let title = IdRef::new(NSString::alloc(nil).init_str(title));
+ let title = IdRef::new(NSString::alloc(nil).init_str(&builder.title));
window.setTitle_(*title);
window.setAcceptsMouseMovedEvents_(YES);
if screen.is_some() {
@@ -464,53 +466,106 @@ impl Window {
}
}
- fn create_context(view: id, builder: &BuilderAttribs) -> Option<IdRef> {
- let profile = match builder.gl_version {
- GlRequest::Latest => NSOpenGLProfileVersion4_1Core as u32,
- GlRequest::Specific(Api::OpenGl, (1 ... 2, _)) => NSOpenGLProfileVersionLegacy as u32,
- GlRequest::Specific(Api::OpenGl, (3, 0)) => NSOpenGLProfileVersionLegacy as u32,
- GlRequest::Specific(Api::OpenGl, (3, 1 ... 2)) => NSOpenGLProfileVersion3_2Core as u32,
- GlRequest::Specific(Api::OpenGl, _) => NSOpenGLProfileVersion4_1Core as u32,
- GlRequest::Specific(_, _) => panic!("Only the OpenGL API is supported"), // FIXME: return Result
- GlRequest::GlThenGles { opengl_version: (1 ... 2, _), .. } => NSOpenGLProfileVersionLegacy as u32,
- GlRequest::GlThenGles { opengl_version: (3, 0), .. } => NSOpenGLProfileVersionLegacy as u32,
- GlRequest::GlThenGles { opengl_version: (3, 1 ... 2), .. } => NSOpenGLProfileVersion3_2Core as u32,
- GlRequest::GlThenGles { .. } => NSOpenGLProfileVersion4_1Core as u32,
+ fn create_context(view: id, builder: &BuilderAttribs) -> Result<(Option<IdRef>, Option<PixelFormat>), CreationError> {
+ let profile = match (builder.gl_version, builder.gl_version.to_gl_version(), builder.gl_profile) {
+ (GlRequest::Latest, _, Some(GlProfile::Compatibility)) => NSOpenGLProfileVersionLegacy as u32,
+ (GlRequest::Latest, _, _) => NSOpenGLProfileVersion4_1Core as u32,
+ (_, Some((1 ... 2, _)), Some(GlProfile::Core)) |
+ (_, Some((3 ... 4, _)), Some(GlProfile::Compatibility)) =>
+ return Err(CreationError::NotSupported),
+ (_, Some((1 ... 2, _)), _) => NSOpenGLProfileVersionLegacy as u32,
+ (_, Some((3, 0 ... 2)), _) => NSOpenGLProfileVersion3_2Core as u32,
+ (_, Some((3 ... 4, _)), _) => NSOpenGLProfileVersion4_1Core as u32,
+ _ => return Err(CreationError::NotSupported),
};
- unsafe {
- let mut attributes = vec![
- NSOpenGLPFADoubleBuffer as u32,
- NSOpenGLPFAClosestPolicy as u32,
- NSOpenGLPFAColorSize as u32, 24,
- NSOpenGLPFAAlphaSize as u32, 8,
- NSOpenGLPFADepthSize as u32, 24,
- NSOpenGLPFAStencilSize as u32, 8,
- NSOpenGLPFAOpenGLProfile as u32, profile,
- ];
-
- if let Some(samples) = builder.multisampling {
- attributes = attributes + &[
- NSOpenGLPFAMultisample as u32,
- NSOpenGLPFASampleBuffers as u32, 1,
- NSOpenGLPFASamples as u32, samples as u32,
- ];
- }
- attributes.push(0);
+ // NOTE: OS X no longer has the concept of setting individual
+ // color component's bit size. Instead we can only specify the
+ // full color size and hope for the best. Another hiccup is that
+ // `NSOpenGLPFAColorSize` also includes `NSOpenGLPFAAlphaSize`,
+ // so we have to account for that as well.
+ let alpha_depth = builder.alpha_bits.unwrap_or(8);
+ let color_depth = builder.color_bits.unwrap_or(24) + alpha_depth;
+
+ let mut attributes = vec![
+ NSOpenGLPFADoubleBuffer as u32,
+ NSOpenGLPFAClosestPolicy as u32,
+ NSOpenGLPFAColorSize as u32, color_depth as u32,
+ NSOpenGLPFAAlphaSize as u32, alpha_depth as u32,
+ NSOpenGLPFADepthSize as u32, builder.depth_bits.unwrap_or(24) as u32,
+ NSOpenGLPFAStencilSize as u32, builder.stencil_bits.unwrap_or(8) as u32,
+ NSOpenGLPFAOpenGLProfile as u32, profile,
+ ];
+
+ // A color depth higher than 64 implies we're using either 16-bit
+ // floats or 32-bit floats and OS X requires a flag to be set
+ // accordingly.
+ if color_depth >= 64 {
+ attributes.push(NSOpenGLPFAColorFloat as u32);
+ }
+ builder.multisampling.map(|samples| {
+ attributes.push(NSOpenGLPFAMultisample as u32);
+ attributes.push(NSOpenGLPFASampleBuffers as u32); attributes.push(1);
+ attributes.push(NSOpenGLPFASamples as u32); attributes.push(samples as u32);
+ });
+
+ // attribute list must be null terminated.
+ attributes.push(0);
+
+ Ok(unsafe {
let pixelformat = IdRef::new(NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attributes));
- pixelformat.non_nil().map(|pixelformat| {
+
+ if let Some(pixelformat) = pixelformat.non_nil() {
+
+ // TODO: Add context sharing
let context = IdRef::new(NSOpenGLContext::alloc(nil).initWithFormat_shareContext_(*pixelformat, nil));
- context.non_nil().map(|context| {
- context.setView_(view);
+
+ if let Some(cxt) = context.non_nil() {
+ let pf = {
+ let get_attr = |attrib: NSOpenGLPixelFormatAttribute| -> i32 {
+ let mut value = 0;
+
+ NSOpenGLPixelFormat::getValues_forAttribute_forVirtualScreen_(
+ *pixelformat,
+ &mut value,
+ attrib,
+ NSOpenGLContext::currentVirtualScreen(*cxt));
+
+ value
+ };
+
+ PixelFormat {
+ hardware_accelerated: get_attr(NSOpenGLPFAAccelerated) != 0,
+ color_bits: (get_attr(NSOpenGLPFAColorSize) - get_attr(NSOpenGLPFAAlphaSize)) as u8,
+ alpha_bits: get_attr(NSOpenGLPFAAlphaSize) as u8,
+ depth_bits: get_attr(NSOpenGLPFADepthSize) as u8,
+ stencil_bits: get_attr(NSOpenGLPFAStencilSize) as u8,
+ stereoscopy: get_attr(NSOpenGLPFAStereo) != 0,
+ double_buffer: get_attr(NSOpenGLPFADoubleBuffer) != 0,
+ multisampling: if get_attr(NSOpenGLPFAMultisample) > 0 {
+ Some(get_attr(NSOpenGLPFASamples) as u16)
+ } else {
+ None
+ },
+ srgb: true,
+ }
+ };
+
+ cxt.setView_(view);
if builder.vsync {
let value = 1;
- context.setValues_forParameter_(&value, NSOpenGLContextParameter::NSOpenGLCPSwapInterval);
+ cxt.setValues_forParameter_(&value, NSOpenGLContextParameter::NSOpenGLCPSwapInterval);
}
- context
- })
- }).unwrap_or(None)
- }
+
+ (Some(cxt), Some(pf))
+ } else {
+ (None, None)
+ }
+ } else {
+ (None, None)
+ }
+ })
}
pub fn is_closed(&self) -> bool {
@@ -608,39 +663,6 @@ impl Window {
return None;
}
- pub unsafe fn make_current(&self) {
- let _: () = msg_send![*self.context, update];
- self.context.makeCurrentContext();
- }
-
- pub fn is_current(&self) -> bool {
- unsafe {
- let current = NSOpenGLContext::currentContext(nil);
- if current != nil {
- let is_equal: BOOL = msg_send![current, isEqual:*self.context];
- is_equal != NO
- } else {
- false
- }
- }
- }
-
- pub fn get_proc_address(&self, _addr: &str) -> *const () {
- let symbol_name: CFString = FromStr::from_str(_addr).unwrap();
- let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap();
- let framework = unsafe {
- CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef())
- };
- let symbol = unsafe {
- CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef())
- };
- symbol as *const ()
- }
-
- pub fn swap_buffers(&self) {
- unsafe { self.context.flushBuffer(); }
- }
-
pub fn platform_display(&self) -> *mut libc::c_void {
unimplemented!()
}
@@ -649,14 +671,6 @@ impl Window {
unimplemented!()
}
- pub fn get_api(&self) -> ::Api {
- ::Api::OpenGl
- }
-
- pub fn get_pixel_format(&self) -> PixelFormat {
- unimplemented!();
- }
-
pub fn set_window_resize_callback(&mut self, callback: Option<fn(u32, u32)>) {
self.delegate.state.resize_handler = callback;
}
@@ -701,9 +715,12 @@ impl Window {
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
let cls = Class::get("NSCursor").unwrap();
+
+ // TODO: Check for errors.
match state {
CursorState::Normal => {
let _: () = unsafe { msg_send![cls, unhide] };
+ let _: i32 = unsafe { CGAssociateMouseAndMouseCursorPosition(true) };
Ok(())
},
CursorState::Hide => {
@@ -711,7 +728,8 @@ impl Window {
Ok(())
},
CursorState::Grab => {
- Err("Mouse grabbing is unimplemented".to_string())
+ let _: i32 = unsafe { CGAssociateMouseAndMouseCursorPosition(false) };
+ Ok(())
}
}
}
@@ -727,6 +745,49 @@ impl Window {
}
}
+impl GlContext for Window {
+ unsafe fn make_current(&self) {
+ let _: () = msg_send![*self.context, update];
+ self.context.makeCurrentContext();
+ }
+
+ fn is_current(&self) -> bool {
+ unsafe {
+ let current = NSOpenGLContext::currentContext(nil);
+ if current != nil {
+ let is_equal: BOOL = msg_send![current, isEqual:*self.context];
+ is_equal != NO
+ } else {
+ false
+ }
+ }
+ }
+
+ fn get_proc_address(&self, addr: &str) -> *const libc::c_void {
+ let symbol_name: CFString = FromStr::from_str(addr).unwrap();
+ let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap();
+ let framework = unsafe {
+ CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef())
+ };
+ let symbol = unsafe {
+ CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef())
+ };
+ symbol as *const _
+ }
+
+ fn swap_buffers(&self) {
+ unsafe { self.context.flushBuffer(); }
+ }
+
+ fn get_api(&self) -> ::Api {
+ ::Api::OpenGl
+ }
+
+ fn get_pixel_format(&self) -> PixelFormat {
+ self.pixel_format.clone()
+ }
+}
+
struct IdRef(id);
impl IdRef {