diff options
Diffstat (limited to 'src/api/cocoa')
-rw-r--r-- | src/api/cocoa/headless.rs | 21 | ||||
-rw-r--r-- | src/api/cocoa/mod.rs | 251 |
2 files changed, 172 insertions, 100 deletions
diff --git a/src/api/cocoa/headless.rs b/src/api/cocoa/headless.rs index 298027f..75cca8d 100644 --- a/src/api/cocoa/headless.rs +++ b/src/api/cocoa/headless.rs @@ -1,6 +1,7 @@ use CreationError; use CreationError::OsError; use BuilderAttribs; +use GlContext; use libc; use std::ptr; @@ -9,6 +10,7 @@ use core_foundation::string::CFString; use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName}; use cocoa::base::{id, nil}; use cocoa::appkit::*; +use PixelFormat; mod gl { include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs")); @@ -56,8 +58,10 @@ impl HeadlessContext { Ok(headless) } +} - pub unsafe fn make_current(&self) { +impl GlContext for HeadlessContext { + unsafe fn make_current(&self) { self.context.makeCurrentContext(); gl::GenFramebuffersEXT(1, &mut framebuffer); @@ -76,11 +80,11 @@ impl HeadlessContext { } } - pub fn is_current(&self) -> bool { + fn is_current(&self) -> bool { unimplemented!() } - pub fn get_proc_address(&self, _addr: &str) -> *const () { + fn get_proc_address(&self, _addr: &str) -> *const libc::c_void { let symbol_name: CFString = _addr.parse().unwrap(); let framework_name: CFString = "com.apple.opengl".parse().unwrap(); let framework = unsafe { @@ -89,12 +93,19 @@ impl HeadlessContext { let symbol = unsafe { CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef()) }; - symbol as *const () + symbol as *const libc::c_void + } + + fn swap_buffers(&self) { } - pub fn get_api(&self) -> ::Api { + fn get_api(&self) -> ::Api { ::Api::OpenGl } + + fn get_pixel_format(&self) -> PixelFormat { + unimplemented!(); + } } unsafe impl Send for HeadlessContext {} 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 { |