diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/api/android/mod.rs | 5 | ||||
-rw-r--r-- | src/api/caca/mod.rs | 11 | ||||
-rw-r--r-- | src/api/cocoa/headless.rs | 8 | ||||
-rw-r--r-- | src/api/cocoa/mod.rs | 56 | ||||
-rw-r--r-- | src/api/egl/mod.rs | 120 | ||||
-rw-r--r-- | src/api/emscripten/mod.rs | 16 | ||||
-rw-r--r-- | src/api/glx/mod.rs | 101 | ||||
-rw-r--r-- | src/api/osmesa/mod.rs | 26 | ||||
-rw-r--r-- | src/api/wayland/context.rs | 34 | ||||
-rw-r--r-- | src/api/wayland/mod.rs | 15 | ||||
-rw-r--r-- | src/api/wgl/mod.rs | 69 | ||||
-rw-r--r-- | src/api/win32/callback.rs | 5 | ||||
-rw-r--r-- | src/api/win32/init.rs | 1 | ||||
-rw-r--r-- | src/api/win32/mod.rs | 38 | ||||
-rw-r--r-- | src/api/x11/window.rs | 103 | ||||
-rw-r--r-- | src/events.rs | 24 | ||||
-rw-r--r-- | src/headless.rs | 14 | ||||
-rw-r--r-- | src/lib.rs | 65 | ||||
-rw-r--r-- | src/platform/android/mod.rs | 4 | ||||
-rw-r--r-- | src/platform/emscripten/mod.rs | 5 | ||||
-rw-r--r-- | src/platform/linux/api_dispatch.rs | 12 | ||||
-rw-r--r-- | src/platform/linux/mod.rs | 5 | ||||
-rw-r--r-- | src/platform/windows/mod.rs | 5 | ||||
-rw-r--r-- | src/window.rs | 22 |
24 files changed, 541 insertions, 223 deletions
diff --git a/src/api/android/mod.rs b/src/api/android/mod.rs index d1281e3..1649772 100644 --- a/src/api/android/mod.rs +++ b/src/api/android/mod.rs @@ -15,6 +15,7 @@ use std::collections::VecDeque; use Api; use BuilderAttribs; +use ContextError; use CursorState; use GlContext; use GlRequest; @@ -213,7 +214,7 @@ unsafe impl Send for Window {} unsafe impl Sync for Window {} impl GlContext for Window { - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { self.context.make_current() } @@ -225,7 +226,7 @@ impl GlContext for Window { self.context.get_proc_address(addr) } - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { self.context.swap_buffers() } diff --git a/src/api/caca/mod.rs b/src/api/caca/mod.rs index 06a6931..3b4018e 100644 --- a/src/api/caca/mod.rs +++ b/src/api/caca/mod.rs @@ -6,6 +6,7 @@ use api::osmesa::{OsMesaContext, OsMesaCreationError}; use Api; use BuilderAttribs; +use ContextError; use CreationError; use Event; use GlContext; @@ -129,10 +130,6 @@ impl Window { }) } - pub fn is_closed(&self) -> bool { - false - } - pub fn set_title(&self, title: &str) { } @@ -209,7 +206,7 @@ impl Window { } impl GlContext for Window { - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { self.opengl.make_current() } @@ -221,7 +218,7 @@ impl GlContext for Window { self.opengl.get_proc_address(addr) } - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { unsafe { let canvas = (self.libcaca.caca_get_canvas)(self.display); let width = (self.libcaca.caca_get_canvas_width)(canvas); @@ -235,6 +232,8 @@ impl GlContext for Window { buffer.as_ptr() as *const _); (self.libcaca.caca_refresh_display)(self.display); }; + + Ok(()) } fn get_api(&self) -> Api { diff --git a/src/api/cocoa/headless.rs b/src/api/cocoa/headless.rs index 75cca8d..7cd0f88 100644 --- a/src/api/cocoa/headless.rs +++ b/src/api/cocoa/headless.rs @@ -1,3 +1,4 @@ +use ContextError; use CreationError; use CreationError::OsError; use BuilderAttribs; @@ -61,7 +62,7 @@ impl HeadlessContext { } impl GlContext for HeadlessContext { - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { self.context.makeCurrentContext(); gl::GenFramebuffersEXT(1, &mut framebuffer); @@ -78,6 +79,8 @@ impl GlContext for HeadlessContext { if status != gl::FRAMEBUFFER_COMPLETE_EXT { panic!("Error while creating the framebuffer"); } + + Ok(()) } fn is_current(&self) -> bool { @@ -96,7 +99,8 @@ impl GlContext for HeadlessContext { symbol as *const libc::c_void } - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { + Ok(()) } fn get_api(&self) -> ::Api { diff --git a/src/api/cocoa/mod.rs b/src/api/cocoa/mod.rs index 511148b..332244e 100644 --- a/src/api/cocoa/mod.rs +++ b/src/api/cocoa/mod.rs @@ -8,15 +8,21 @@ use libc; use Api; use BuilderAttribs; +use ContextError; use GlContext; use GlProfile; use GlRequest; use PixelFormat; +use Robustness; use native_monitor::NativeMonitorId; use objc::runtime::{Class, Object, Sel, BOOL, YES, NO}; use objc::declare::ClassDecl; +use cgl; +use cgl::{CGLEnable, kCGLCECrashOnRemovedFunctions, CGLSetParameter, kCGLCPSurfaceOpacity}; +use cgl::CGLContextObj as CGL_CGLContextObj; + use cocoa::base::{id, nil}; use cocoa::foundation::{NSAutoreleasePool, NSDate, NSDefaultRunLoopMode, NSPoint, NSRect, NSSize, NSString, NSUInteger}; @@ -55,7 +61,6 @@ static mut win_pressed: bool = false; static mut alt_pressed: bool = false; struct DelegateState { - is_closed: bool, context: IdRef, view: IdRef, window: IdRef, @@ -79,8 +84,6 @@ impl WindowDelegate { unsafe { let state: *mut libc::c_void = *this.get_ivar("glutinState"); let state = state as *mut DelegateState; - (*state).is_closed = true; - (*state).pending_events.lock().unwrap().push_back(Closed); } YES @@ -275,7 +278,15 @@ impl<'a> Iterator for PollEventsIterator<'a> { self.window.delegate.state.pending_events.lock().unwrap().extend(events.into_iter()); event }, - NSScrollWheel => { Some(MouseWheel(event.scrollingDeltaX() as f64, event.scrollingDeltaY() as f64)) }, + NSScrollWheel => { + use events::MouseScrollDelta::{LineDelta, PixelDelta}; + let delta = if event.hasPreciseScrollingDeltas() == YES { + PixelDelta(event.scrollingDeltaX() as f32, event.scrollingDeltaY() as f32) + } else { + LineDelta(event.scrollingDeltaX() as f32, event.scrollingDeltaY() as f32) + }; + Some(MouseWheel(delta)) + }, _ => { None }, }; @@ -322,6 +333,13 @@ impl Window { unimplemented!() } + match builder.gl_robustness { + Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + return Err(CreationError::NotSupported); + }, + _ => () + } + let app = match Window::create_app() { Some(app) => app, None => { return Err(OsError(format!("Couldn't create NSApplication"))); }, @@ -345,6 +363,21 @@ impl Window { }; unsafe { + if builder.transparent { + let clear_col = { + let cls = Class::get("NSColor").unwrap(); + + msg_send![cls, clearColor] + }; + window.setOpaque_(NO); + window.setBackgroundColor_(clear_col); + + let obj = context.CGLContextObj(); + + let mut opacity = 0; + CGLSetParameter(obj, kCGLCPSurfaceOpacity, &mut opacity); + } + app.activateIgnoringOtherApps_(YES); if builder.visible { window.makeKeyAndOrderFront_(nil); @@ -354,7 +387,6 @@ impl Window { } let ds = DelegateState { - is_closed: false, context: context.clone(), view: view.clone(), window: window.clone(), @@ -425,7 +457,7 @@ impl Window { } }; - let masks = if screen.is_some() { + let masks = if screen.is_some() || !builder.decorations { NSBorderlessWindowMask as NSUInteger } else { NSTitledWindowMask as NSUInteger | @@ -564,6 +596,8 @@ impl Window { let value = if builder.vsync { 1 } else { 0 }; cxt.setValues_forParameter_(&value, NSOpenGLContextParameter::NSOpenGLCPSwapInterval); + CGLEnable(cxt.CGLContextObj(), kCGLCECrashOnRemovedFunctions); + Ok((cxt, pf)) } else { Err(CreationError::NotSupported) @@ -574,10 +608,6 @@ impl Window { } } - pub fn is_closed(&self) -> bool { - self.delegate.state.is_closed - } - pub fn set_title(&self, title: &str) { unsafe { let title = IdRef::new(NSString::alloc(nil).init_str(title)); @@ -752,9 +782,10 @@ impl Window { } impl GlContext for Window { - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { let _: () = msg_send![*self.context, update]; self.context.makeCurrentContext(); + Ok(()) } fn is_current(&self) -> bool { @@ -781,8 +812,9 @@ impl GlContext for Window { symbol as *const _ } - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { unsafe { self.context.flushBuffer(); } + Ok(()) } fn get_api(&self) -> ::Api { diff --git a/src/api/egl/mod.rs b/src/api/egl/mod.rs index 3335de0..1a6cc06 100644 --- a/src/api/egl/mod.rs +++ b/src/api/egl/mod.rs @@ -2,10 +2,12 @@ #![allow(unused_variables)] use BuilderAttribs; +use ContextError; use CreationError; use GlContext; use GlRequest; use PixelFormat; +use Robustness; use Api; use libc; @@ -115,15 +117,16 @@ impl Context { let context = unsafe { if let Some(version) = version { try!(create_context(&egl, display, &egl_version, api, version, config_id, - builder.gl_debug).map_err(|_| CreationError::NotSupported)) + builder.gl_debug, builder.gl_robustness)) } else if api == Api::OpenGlEs { if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (2, 0), - config_id, builder.gl_debug) + config_id, builder.gl_debug, builder.gl_robustness) { ctxt } else if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (1, 0), - config_id, builder.gl_debug) + config_id, builder.gl_debug, + builder.gl_robustness) { ctxt } else { @@ -132,15 +135,17 @@ impl Context { } else { if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (3, 2), - config_id, builder.gl_debug) + config_id, builder.gl_debug, builder.gl_robustness) { ctxt } else if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (3, 1), - config_id, builder.gl_debug) + config_id, builder.gl_debug, + builder.gl_robustness) { ctxt } else if let Ok(ctxt) = create_context(&egl, display, &egl_version, api, (1, 0), - config_id, builder.gl_debug) + config_id, builder.gl_debug, + builder.gl_robustness) { ctxt } else { @@ -161,11 +166,18 @@ impl Context { } impl GlContext for Context { - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { let ret = self.egl.MakeCurrent(self.display, self.surface, self.surface, self.context); if ret == 0 { - panic!("eglMakeCurrent failed"); + if self.egl.GetError() as u32 == ffi::egl::CONTEXT_LOST { + return Err(ContextError::ContextLost); + } else { + panic!("eglMakeCurrent failed"); + } + + } else { + Ok(()) } } @@ -181,13 +193,20 @@ impl GlContext for Context { } } - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { let ret = unsafe { self.egl.SwapBuffers(self.display, self.surface) }; if ret == 0 { - panic!("eglSwapBuffers failed"); + if unsafe { self.egl.GetError() } as u32 == ffi::egl::CONTEXT_LOST { + return Err(ContextError::ContextLost); + } else { + panic!("eglSwapBuffers failed"); + } + + } else { + Ok(()) } } @@ -320,8 +339,8 @@ unsafe fn enumerate_configs(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDi unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisplay, egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), api: Api, version: (u8, u8), config_id: ffi::egl::types::EGLConfig, - gl_debug: bool) - -> Result<ffi::egl::types::EGLContext, ()> + gl_debug: bool, gl_robustness: Robustness) + -> Result<ffi::egl::types::EGLContext, CreationError> { let extensions = if egl_version >= &(1, 2) { let p = CStr::from_ptr(egl.QueryString(display, ffi::egl::EXTENSIONS as i32)); @@ -330,7 +349,8 @@ unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDispl format!("") }; - let mut context_attributes = vec![]; + let mut context_attributes = Vec::with_capacity(10); + let mut flags = 0; if egl_version >= &(1, 5) || extensions.contains("EGL_KHR_create_context ") || @@ -341,17 +361,85 @@ unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDispl context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32); context_attributes.push(version.1 as i32); + // handling robustness + let supports_robustness = egl_version >= &(1, 5) || + extensions.contains("EGL_EXT_create_context_robustness ") || + extensions.ends_with("EGL_EXT_create_context_robustness"); + + match gl_robustness { + Robustness::NotRobust => (), + + Robustness::NoError => { + if extensions.contains("EGL_KHR_create_context_no_error ") || + extensions.ends_with("EGL_KHR_create_context_no_error") + { + context_attributes.push(ffi::egl::CONTEXT_OPENGL_NO_ERROR_KHR as libc::c_int); + context_attributes.push(1); + } + }, + + Robustness::RobustNoResetNotification => { + if supports_robustness { + context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY + as libc::c_int); + context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as libc::c_int); + flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as libc::c_int; + } else { + return Err(CreationError::NotSupported); + } + }, + + Robustness::TryRobustNoResetNotification => { + if supports_robustness { + context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY + as libc::c_int); + context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as libc::c_int); + flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as libc::c_int; + } + }, + + Robustness::RobustLoseContextOnReset => { + if supports_robustness { + context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY + as libc::c_int); + context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as libc::c_int); + flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as libc::c_int; + } else { + return Err(CreationError::NotSupported); + } + }, + + Robustness::TryRobustLoseContextOnReset => { + if supports_robustness { + context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY + as libc::c_int); + context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as libc::c_int); + flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as libc::c_int; + } + }, + } + if gl_debug { if egl_version >= &(1, 5) { context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32); context_attributes.push(ffi::egl::TRUE as i32); } else { - context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32); - context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32); + flags = flags | ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32; } } + context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32); + context_attributes.push(flags); + } else if egl_version >= &(1, 3) && api == Api::OpenGlEs { + // robustness is not supported + match gl_robustness { + Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + return Err(CreationError::NotSupported); + }, + _ => () + } + context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); context_attributes.push(version.0 as i32); } @@ -362,7 +450,7 @@ unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDispl context_attributes.as_ptr()); if context.is_null() { - return Err(()); + return Err(CreationError::NotSupported); } Ok(context) diff --git a/src/api/emscripten/mod.rs b/src/api/emscripten/mod.rs index 48b31a9..79dda6b 100644 --- a/src/api/emscripten/mod.rs +++ b/src/api/emscripten/mod.rs @@ -5,6 +5,7 @@ use libc; use {Event, BuilderAttribs, CreationError, MouseCursor}; use Api; use PixelFormat; +use ContextError; use GlContext; use std::collections::VecDeque; @@ -108,11 +109,6 @@ impl Window { }) } - pub fn is_closed(&self) -> bool { - use std::ptr; - unsafe { ffi::emscripten_is_webgl_context_lost(ptr::null()) != 0 } - } - pub fn set_title(&self, _title: &str) { } @@ -191,9 +187,10 @@ impl Window { } impl GlContext for Window { - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { // TOOD: check if == EMSCRIPTEN_RESULT ffi::emscripten_webgl_make_context_current(self.context); + Ok(()) } fn is_current(&self) -> bool { @@ -209,10 +206,9 @@ impl GlContext for Window { } } - fn swap_buffers(&self) { - unsafe { - ffi::emscripten_sleep(1); // FIXME: - } + fn swap_buffers(&self) -> Result<(), ContextError> { + unsafe { ffi::emscripten_sleep(1); } // FIXME: + Ok(()) } fn get_api(&self) -> Api { diff --git a/src/api/glx/mod.rs b/src/api/glx/mod.rs index f1eb436..d286813 100644 --- a/src/api/glx/mod.rs +++ b/src/api/glx/mod.rs @@ -1,15 +1,17 @@ #![cfg(all(target_os = "linux", feature = "window"))] use BuilderAttribs; +use ContextError; use CreationError; use GlContext; use GlProfile; use GlRequest; use Api; use PixelFormat; +use Robustness; use libc; -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::{mem, ptr}; use api::x11::ffi; @@ -47,6 +49,13 @@ impl Context { ptr::null() }; + // loading the list of extensions + let extensions = unsafe { + let extensions = glx.QueryExtensionsString(display as *mut _, 0); // FIXME: screen number + let extensions = CStr::from_ptr(extensions).to_bytes().to_vec(); + String::from_utf8(extensions).unwrap() + }; + // loading the extra GLX functions let extra_functions = ffi::glx_extra::Glx::load_with(|addr| { with_c_str(addr, |s| { @@ -57,32 +66,35 @@ impl Context { // creating GL context let context = match builder.gl_version { GlRequest::Latest => { - if let Ok(ctxt) = create_context(&glx, &extra_functions, (3, 2), - builder.gl_profile, builder.gl_debug, share, + if let Ok(ctxt) = create_context(&glx, &extra_functions, &extensions, (3, 2), + builder.gl_profile, builder.gl_debug, + builder.gl_robustness, share, display, fb_config, &mut visual_infos) { ctxt - } else if let Ok(ctxt) = create_context(&glx, &extra_functions, (3, 1), + } else if let Ok(ctxt) = create_context(&glx, &extra_functions, &extensions, (3, 1), builder.gl_profile, builder.gl_debug, - share, display, fb_config, - &mut visual_infos) + builder.gl_robustness, share, display, + fb_config, &mut visual_infos) { ctxt } else { - try!(create_context(&glx, &extra_functions, (1, 0), builder.gl_profile, - builder.gl_debug, share, display, fb_config, - &mut visual_infos)) + try!(create_context(&glx, &extra_functions, &extensions, (1, 0), + builder.gl_profile, builder.gl_debug, builder.gl_robustness, + share, display, fb_config, &mut visual_infos)) } }, GlRequest::Specific(Api::OpenGl, (major, minor)) => { - try!(create_context(&glx, &extra_functions, (major, minor), builder.gl_profile, - builder.gl_debug, share, display, fb_config, &mut visual_infos)) + try!(create_context(&glx, &extra_functions, &extensions, (major, minor), + builder.gl_profile, builder.gl_debug, builder.gl_robustness, + share, display, fb_config, &mut visual_infos)) }, GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"), GlRequest::GlThenGles { opengl_version: (major, minor), .. } => { - try!(create_context(&glx, &extra_functions, (major, minor), builder.gl_profile, - builder.gl_debug, share, display, fb_config, &mut visual_infos)) + try!(create_context(&glx, &extra_functions, &extensions, (major, minor), + builder.gl_profile, builder.gl_debug, builder.gl_robustness, + share, display, fb_config, &mut visual_infos)) }, }; @@ -139,11 +151,13 @@ impl Context { } impl GlContext for Context { - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { + // TODO: glutin needs some internal changes for proper error recovery let res = self.glx.MakeCurrent(self.display as *mut _, self.window, self.context); if res == 0 { panic!("glx::MakeCurrent failed"); } + Ok(()) } fn is_current(&self) -> bool { @@ -158,10 +172,10 @@ impl GlContext for Context { } } - fn swap_buffers(&self) { - unsafe { - self.glx.SwapBuffers(self.display as *mut _, self.window) - } + fn swap_buffers(&self) -> Result<(), ContextError> { + // TODO: glutin needs some internal changes for proper error recovery + unsafe { self.glx.SwapBuffers(self.display as *mut _, self.window); } + Ok(()) } fn get_api(&self) -> ::Api { @@ -179,16 +193,18 @@ unsafe impl Sync for Context {} impl Drop for Context { fn drop(&mut self) { unsafe { - // we don't call MakeCurrent(0, 0) because we are not sure that the context - // is still the current one + if self.is_current() { + self.glx.MakeCurrent(self.display as *mut _, 0, ptr::null_mut()); + } + self.glx.DestroyContext(self.display as *mut _, self.context); } } } -fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, +fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, extensions: &str, version: (u8, u8), profile: Option<GlProfile>, debug: bool, - share: ffi::GLXContext, display: *mut ffi::Display, + robustness: Robustness, share: ffi::GLXContext, display: *mut ffi::Display, fb_config: ffi::glx::types::GLXFBConfig, visual_infos: &mut ffi::glx::types::XVisualInfo) -> Result<ffi::GLXContext, CreationError> @@ -214,10 +230,43 @@ fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, attributes.push(flag as libc::c_int); } - if debug { - attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as libc::c_int); - attributes.push(ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int); - } + let flags = { + let mut flags = 0; + + // robustness + if extensions.split(' ').find(|&i| i == "GLX_ARB_create_context_robustness").is_some() { + match robustness { + Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => { + attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int); + attributes.push(ffi::glx_extra::NO_RESET_NOTIFICATION_ARB as libc::c_int); + flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int; + }, + Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => { + attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int); + attributes.push(ffi::glx_extra::LOSE_CONTEXT_ON_RESET_ARB as libc::c_int); + flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int; + }, + Robustness::NotRobust => (), + Robustness::NoError => (), + } + } else { + match robustness { + Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + return Err(CreationError::NotSupported); + }, + _ => () + } + } + + if debug { + flags = flags | ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int; + } + + flags + }; + + attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as libc::c_int); + attributes.push(flags); attributes.push(0); diff --git a/src/api/osmesa/mod.rs b/src/api/osmesa/mod.rs index 26808f7..db0a1e7 100644 --- a/src/api/osmesa/mod.rs +++ b/src/api/osmesa/mod.rs @@ -4,9 +4,11 @@ extern crate osmesa_sys; use Api; use BuilderAttribs; +use ContextError; use CreationError; use GlContext; use PixelFormat; +use Robustness; use libc; use std::{mem, ptr}; use std::ffi::CString; @@ -37,6 +39,13 @@ impl OsMesaContext { let dimensions = builder.dimensions.unwrap(); + match builder.gl_robustness { + Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + return Err(CreationError::NotSupported.into()); + }, + _ => () + } + Ok(OsMesaContext { width: dimensions.0, height: dimensions.1, @@ -67,14 +76,18 @@ impl OsMesaContext { } impl GlContext for OsMesaContext { - unsafe fn make_current(&self) { - let ret = osmesa_sys::OSMesaMakeCurrent(self.context, - self.buffer.as_ptr() as *mut libc::c_void, - 0x1401, self.width as libc::c_int, self.height as libc::c_int); + unsafe fn make_current(&self) -> Result<(), ContextError> { + let ret = osmesa_sys::OSMesaMakeCurrent(self.context, self.buffer.as_ptr() + as *mut libc::c_void, 0x1401, self.width + as libc::c_int, self.height as libc::c_int); + // an error can only happen in case of invalid parameter, which would indicate a bug + // in glutin if ret == 0 { - panic!("OSMesaMakeCurrent failed") + panic!("OSMesaMakeCurrent failed"); } + + Ok(()) } fn is_current(&self) -> bool { @@ -88,7 +101,8 @@ impl GlContext for OsMesaContext { } } - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { + Ok(()) } fn get_api(&self) -> Api { diff --git a/src/api/wayland/context.rs b/src/api/wayland/context.rs index 3b6c0b8..1af6311 100644 --- a/src/api/wayland/context.rs +++ b/src/api/wayland/context.rs @@ -1,6 +1,8 @@ -use super::wayland::core::{Display, Registry, Compositor, Shell, Output, ButtonState, - Seat, Pointer, default_display, WSurface, SurfaceId, Keyboard, - KeyState}; +use super::wayland::core::{default_display, Display, Registry}; +use super::wayland::core::compositor::{Compositor, SurfaceId, WSurface}; +use super::wayland::core::output::Output; +use super::wayland::core::seat::{ButtonState, Seat, Pointer, Keyboard, KeyState}; +use super::wayland::core::shell::Shell; use super::wayland_kbd::MappedKeyboard; use super::keyboard::keycode_to_vkey; @@ -68,11 +70,11 @@ impl WaylandContext { if let Some(ref mut p) = pointer { // set the enter/leave callbacks let current_surface = current_pointer_surface.clone(); - p.set_enter_action(move |_, sid, x, y| { + p.set_enter_action(move |_, _, sid, x, y| { *current_surface.lock().unwrap() = Some(sid); }); let current_surface = current_pointer_surface.clone(); - p.set_leave_action(move |_, sid| { + p.set_leave_action(move |_, _, sid| { *current_surface.lock().unwrap() = None; }); // set the events callbacks @@ -90,7 +92,7 @@ impl WaylandContext { }); let current_surface = current_pointer_surface.clone(); let event_queues = windows_event_queues.clone(); - p.set_button_action(move |_, sid, b, s| { + p.set_button_action(move |_, _, sid, b, s| { let button = match b { 0x110 => MouseButton::Left, 0x111 => MouseButton::Right, @@ -98,8 +100,8 @@ impl WaylandContext { _ => return }; let state = match s { - ButtonState::WL_POINTER_BUTTON_STATE_RELEASED => ElementState::Released, - ButtonState::WL_POINTER_BUTTON_STATE_PRESSED => ElementState::Pressed + ButtonState::Released => ElementState::Released, + ButtonState::Pressed => ElementState::Pressed }; // dispatch to the appropriate queue let sid = *current_surface.lock().unwrap(); @@ -119,11 +121,11 @@ impl WaylandContext { display.sync_roundtrip(); let current_surface = current_keyboard_surface.clone(); - wkbd.set_enter_action(move |_, sid, _| { + wkbd.set_enter_action(move |_, _, sid, _| { *current_surface.lock().unwrap() = Some(sid); }); let current_surface = current_keyboard_surface.clone(); - wkbd.set_leave_action(move |_, sid| { + wkbd.set_leave_action(move |_, _, sid| { *current_surface.lock().unwrap() = None; }); @@ -132,10 +134,10 @@ impl WaylandContext { // We managed to load a keymap let current_surface = current_keyboard_surface.clone(); let event_queues = windows_event_queues.clone(); - mkbd.set_key_action(move |state, _, _, keycode, keystate| { + mkbd.set_key_action(move |state, _, _, _, keycode, keystate| { let kstate = match keystate { - KeyState::WL_KEYBOARD_KEY_STATE_RELEASED => ElementState::Released, - KeyState::WL_KEYBOARD_KEY_STATE_PRESSED => ElementState::Pressed + KeyState::Released => ElementState::Released, + KeyState::Pressed => ElementState::Pressed }; let mut events = Vec::new(); // key event @@ -167,10 +169,10 @@ impl WaylandContext { // fallback to raw inputs, no virtual keycodes let current_surface = current_keyboard_surface.clone(); let event_queues = windows_event_queues.clone(); - rkbd.set_key_action(move |_, _, keycode, keystate| { + rkbd.set_key_action(move |_, _, _, keycode, keystate| { let kstate = match keystate { - KeyState::WL_KEYBOARD_KEY_STATE_RELEASED => ElementState::Released, - KeyState::WL_KEYBOARD_KEY_STATE_PRESSED => ElementState::Pressed + KeyState::Released => ElementState::Released, + KeyState::Pressed => ElementState::Pressed }; let event = Event::KeyboardInput(kstate, (keycode & 0xff) as u8, None); // dispatch to the appropriate queue diff --git a/src/api/wayland/mod.rs b/src/api/wayland/mod.rs index b9bfa18..0af5496 100644 --- a/src/api/wayland/mod.rs +++ b/src/api/wayland/mod.rs @@ -2,13 +2,16 @@ #![allow(unused_variables, dead_code)] use self::wayland::egl::{EGLSurface, is_egl_available}; -use self::wayland::core::{ShellSurface, Surface, Output, ShellFullscreenMethod}; +use self::wayland::core::Surface; +use self::wayland::core::output::Output; +use self::wayland::core::shell::{ShellSurface, ShellFullscreenMethod}; use libc; use api::dlopen; use api::egl::Context as EglContext; use BuilderAttribs; +use ContextError; use CreationError; use Event; use PixelFormat; @@ -184,11 +187,6 @@ impl Window { }) } - pub fn is_closed(&self) -> bool { - // TODO - false - } - pub fn set_title(&self, title: &str) { let ctitle = CString::new(title).unwrap(); self.shell_surface.set_title(&ctitle); @@ -282,8 +280,7 @@ impl Window { } impl GlContext for Window { - - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { self.context.make_current() } @@ -295,7 +292,7 @@ impl GlContext for Window { self.context.get_proc_address(addr) } - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { self.context.swap_buffers() } diff --git a/src/api/wgl/mod.rs b/src/api/wgl/mod.rs index d1f42d2..cf09ad0 100644 --- a/src/api/wgl/mod.rs +++ b/src/api/wgl/mod.rs @@ -1,11 +1,13 @@ #![cfg(any(target_os = "windows"))] use BuilderAttribs; +use ContextError; use CreationError; use GlContext; use GlRequest; use GlProfile; use PixelFormat; +use Robustness; use Api; use self::make_current_guard::CurrentContextGuard; @@ -130,9 +132,7 @@ impl Context { // handling vsync if builder.vsync { - // contrary to most extensions, it is permitted to discover the presence of - // `WGL_EXT_swap_control` by seeing if the function pointer is available - if extra_functions.SwapIntervalEXT.is_loaded() { + if extensions.split(' ').find(|&i| i == "WGL_EXT_swap_control").is_some() { let _guard = try!(CurrentContextGuard::make_current(hdc, context.0)); if extra_functions.SwapIntervalEXT(1) == 0 { @@ -156,9 +156,12 @@ impl Context { } impl GlContext for Context { - unsafe fn make_current(&self) { - // TODO: check return value - gl::wgl::MakeCurrent(self.hdc as *const _, self.context.0 as *const _); + unsafe fn make_current(&self) -> Result<(), ContextError> { + if gl::wgl::MakeCurrent(self.hdc as *const _, self.context.0 as *const _) != 0 { + Ok(()) + } else { + Err(ContextError::IoError(io::Error::last_os_error())) + } } fn is_current(&self) -> bool { @@ -176,10 +179,15 @@ impl GlContext for Context { } } - fn swap_buffers(&self) { - unsafe { - gdi32::SwapBuffers(self.hdc); - } + fn swap_buffers(&self) -> Result<(), ContextError> { + // TODO: decide how to handle the error + /*if unsafe { gdi32::SwapBuffers(self.hdc) } != 0 { + Ok(()) + } else { + Err(ContextError::IoError(io::Error::last_os_error())) + }*/ + unsafe { gdi32::SwapBuffers(self.hdc) }; + Ok(()) } fn get_api(&self) -> Api { @@ -260,10 +268,43 @@ unsafe fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &BuilderAttribs<'st } } - if builder.gl_debug { - attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as libc::c_int); - attributes.push(gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int); - } + let flags = { + let mut flags = 0; + + // robustness + if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context_robustness").is_some() { + match builder.gl_robustness { + Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => { + attributes.push(gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int); + attributes.push(gl::wgl_extra::NO_RESET_NOTIFICATION_ARB as libc::c_int); + flags = flags | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int; + }, + Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => { + attributes.push(gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as libc::c_int); + attributes.push(gl::wgl_extra::LOSE_CONTEXT_ON_RESET_ARB as libc::c_int); + flags = flags | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as libc::c_int; + }, + Robustness::NotRobust => (), + Robustness::NoError => (), + } + } else { + match builder.gl_robustness { + Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + return Err(CreationError::NotSupported); + }, + _ => () + } + } + + if builder.gl_debug { + flags = flags | gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int; + } + + flags + }; + + attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as libc::c_int); + attributes.push(flags); attributes.push(0); diff --git a/src/api/win32/callback.rs b/src/api/win32/callback.rs index 86d5f1c..6ac56f3 100644 --- a/src/api/win32/callback.rs +++ b/src/api/win32/callback.rs @@ -112,12 +112,13 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT, winapi::WM_MOUSEWHEEL => { use events::Event::MouseWheel; + use events::MouseScrollDelta::LineDelta; let value = (wparam >> 16) as i16; let value = value as i32; - let value = value as f64 / winapi::WHEEL_DELTA as f64; + let value = value as f32 / winapi::WHEEL_DELTA as f32; - send_event(window, MouseWheel(0.0, value)); + send_event(window, MouseWheel(LineDelta(0.0, value))); 0 }, diff --git a/src/api/win32/init.rs b/src/api/win32/init.rs index f46c395..7cb5433 100644 --- a/src/api/win32/init.rs +++ b/src/api/win32/init.rs @@ -257,7 +257,6 @@ unsafe fn init(title: Vec<u16>, builder: BuilderAttribs<'static>, window: real_window, context: context, events_receiver: events_receiver, - is_closed: AtomicBool::new(false), cursor_state: cursor_state, }) } diff --git a/src/api/win32/mod.rs b/src/api/win32/mod.rs index af339c5..d21f16d 100644 --- a/src/api/win32/mod.rs +++ b/src/api/win32/mod.rs @@ -11,6 +11,7 @@ use std::sync::{ }; use std::sync::mpsc::Receiver; use libc; +use ContextError; use {CreationError, Event, MouseCursor}; use CursorState; use GlContext; @@ -47,9 +48,6 @@ pub struct Window { /// Receiver for the events dispatched by the window callback. events_receiver: Receiver<Event>, - /// True if a `Closed` event has been received. - is_closed: AtomicBool, - /// The current cursor state. cursor_state: Arc<Mutex<CursorState>>, } @@ -98,12 +96,6 @@ impl Window { } /// See the docs in the crate root file. - pub fn is_closed(&self) -> bool { - use std::sync::atomic::Ordering::Relaxed; - self.is_closed.load(Relaxed) - } - - /// See the docs in the crate root file. /// /// Calls SetWindowText on the HWND. pub fn set_title(&self, text: &str) { @@ -315,7 +307,7 @@ impl Window { } impl GlContext for Window { - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { match self.context { Context::Wgl(ref c) => c.make_current(), Context::Egl(ref c) => c.make_current(), @@ -336,7 +328,7 @@ impl GlContext for Window { } } - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { match self.context { Context::Wgl(ref c) => c.swap_buffers(), Context::Egl(ref c) => c.swap_buffers(), @@ -366,17 +358,7 @@ impl<'a> Iterator for PollEventsIterator<'a> { type Item = Event; fn next(&mut self) -> Option<Event> { - use events::Event::Closed; - - match self.window.events_receiver.try_recv() { - Ok(Closed) => { - use std::sync::atomic::Ordering::Relaxed; - self.window.is_closed.store(true, Relaxed); - Some(Closed) - }, - Ok(ev) => Some(ev), - Err(_) => None - } + self.window.events_receiver.try_recv().ok() } } @@ -388,17 +370,7 @@ impl<'a> Iterator for WaitEventsIterator<'a> { type Item = Event; fn next(&mut self) -> Option<Event> { - use events::Event::Closed; - - match self.window.events_receiver.recv() { - Ok(Closed) => { - use std::sync::atomic::Ordering::Relaxed; - self.window.is_closed.store(true, Relaxed); - Some(Closed) - }, - Ok(ev) => Some(ev), - Err(_) => None - } + self.window.events_receiver.recv().ok() } } diff --git a/src/api/x11/window.rs b/src/api/x11/window.rs index 42134d0..15e27ce 100644 --- a/src/api/x11/window.rs +++ b/src/api/x11/window.rs @@ -9,6 +9,7 @@ use std::collections::VecDeque; use std::sync::{Arc, Mutex}; use Api; +use ContextError; use CursorState; use GlContext; use GlRequest; @@ -43,6 +44,7 @@ pub struct XWindow { xf86_desk_mode: *mut ffi::XF86VidModeModeInfo, ic: ffi::XIC, im: ffi::XIM, + colormap: ffi::Colormap, } pub enum Context { @@ -64,6 +66,8 @@ impl Drop for XWindow { // is still the current one self.context = Context::None; + let _lock = GLOBAL_XOPENIM_LOCK.lock().unwrap(); + if self.is_fullscreen { (self.display.xf86vmode.XF86VidModeSwitchToMode)(self.display.display, self.screen_id, self.xf86_desk_mode); (self.display.xf86vmode.XF86VidModeSetViewPort)(self.display.display, self.screen_id, 0, 0); @@ -72,6 +76,7 @@ impl Drop for XWindow { (self.display.xlib.XDestroyIC)(self.ic); (self.display.xlib.XCloseIM)(self.im); (self.display.xlib.XDestroyWindow)(self.display.display, self.window); + (self.display.xlib.XFreeColormap)(self.display.display, self.colormap); } } } @@ -215,6 +220,7 @@ impl<'a> Iterator for PollEventsIterator<'a> { use events::Event::{MouseInput, MouseWheel}; use events::ElementState::{Pressed, Released}; use events::MouseButton::{Left, Right, Middle}; + use events::MouseScrollDelta::{LineDelta}; let event: &ffi::XButtonEvent = unsafe { mem::transmute(&xev) }; @@ -225,11 +231,13 @@ impl<'a> Iterator for PollEventsIterator<'a> { ffi::Button2 => Some(Middle), ffi::Button3 => Some(Right), ffi::Button4 => { - self.window.pending_events.lock().unwrap().push_back(MouseWheel(0.0, 1.0)); + let delta = LineDelta(0.0, 1.0); + self.window.pending_events.lock().unwrap().push_back(MouseWheel(delta)); None } ffi::Button5 => { - self.window.pending_events.lock().unwrap().push_back(MouseWheel(0.0, -1.0)); + let delta = LineDelta(0.0, -1.0); + self.window.pending_events.lock().unwrap().push_back(MouseWheel(delta)); None } _ => None @@ -256,9 +264,10 @@ impl<'a> Iterator for WaitEventsIterator<'a> { type Item = Event; fn next(&mut self) -> Option<Event> { + use std::sync::atomic::Ordering::Relaxed; use std::mem; - while !self.window.is_closed() { + while !self.window.is_closed.load(Relaxed) { if let Some(ev) = self.window.pending_events.lock().unwrap().pop_front() { return Some(ev); } @@ -335,34 +344,65 @@ impl Window { if fb.is_null() { return Err(OsError(format!("glx::ChooseFBConfig failed"))); } - let preferred_fb = *fb; // TODO: choose more wisely + + let preferred_fb = if builder.transparent { + let mut best_fbi_for_transparent = 0isize; + + for i in 0isize..num_fb as isize { + let vi = display.glx.as_ref().unwrap().GetVisualFromFBConfig(display.display as *mut _, *fb.offset(i)); + if (*vi).depth == 32 { + best_fbi_for_transparent = i; + break; + } + } + + *fb.offset(best_fbi_for_transparent) + } else { + *fb // TODO: choose more wisely + }; + (display.xlib.XFree)(fb as *mut _); preferred_fb }; - let mut best_mode = -1; - let modes = unsafe { + // finding the mode to switch to if necessary + let (mode_to_switch_to, xf86_desk_mode) = unsafe { let mut mode_num: libc::c_int = mem::uninitialized(); let mut modes: *mut *mut ffi::XF86VidModeModeInfo = mem::uninitialized(); if (display.xf86vmode.XF86VidModeGetAllModeLines)(display.display, screen_id, &mut mode_num, &mut modes) == 0 { return Err(OsError(format!("Could not query the video modes"))); } - for i in 0..mode_num { - let mode: ffi::XF86VidModeModeInfo = ptr::read(*modes.offset(i as isize) as *const _); - if mode.hdisplay == dimensions.0 as u16 && mode.vdisplay == dimensions.1 as u16 { - best_mode = i; + let xf86_desk_mode = *modes.offset(0); + + // FIXME: `XF86VidModeModeInfo` is missing its `hskew` field. Therefore we point to + // `vsyncstart` instead of `vdisplay` as a temporary hack. + + let mode_to_switch_to = if builder.monitor.is_some() { + let matching_mode = (0 .. mode_num).map(|i| { + let m: ffi::XF86VidModeModeInfo = ptr::read(*modes.offset(i as isize) as *const _); m + }).find(|m| m.hdisplay == dimensions.0 as u16 && m.vsyncstart == dimensions.1 as u16); + + if let Some(matching_mode) = matching_mode { + Some(matching_mode) + + } else { + let m = (0 .. mode_num).map(|i| { + let m: ffi::XF86VidModeModeInfo = ptr::read(*modes.offset(i as isize) as *const _); m + }).find(|m| m.hdisplay >= dimensions.0 as u16 && m.vsyncstart == dimensions.1 as u16); + + match m { + Some(m) => Some(m), + None => return Err(OsError(format!("Could not find a suitable graphics mode"))) + } } + } else { + None }; - if best_mode == -1 && builder.monitor.is_some() { - return Err(OsError(format!("Could not find a suitable graphics mode"))); - } - modes - }; + (display.xlib.XFree)(modes as *mut _); - let xf86_desk_mode = unsafe { - *modes.offset(0) + (mode_to_switch_to, xf86_desk_mode) }; // getting the visual infos @@ -421,15 +461,24 @@ impl Window { ffi::KeyReleaseMask | ffi::ButtonPressMask | ffi::ButtonReleaseMask | ffi::KeymapStateMask; swa.border_pixel = 0; + if builder.transparent { + swa.background_pixel = 0; + } swa.override_redirect = 0; swa }; - let mut window_attributes = ffi::CWBorderPixel | ffi::CWColormap | ffi:: CWEventMask; - if builder.monitor.is_some() { + let mut window_attributes = ffi::CWBorderPixel | ffi::CWColormap | ffi::CWEventMask; + + if builder.transparent { + window_attributes |= ffi::CWBackPixel; + } + + // switching to fullscreen + if let Some(mut mode_to_switch_to) = mode_to_switch_to { window_attributes |= ffi::CWOverrideRedirect; unsafe { - (display.xf86vmode.XF86VidModeSwitchToMode)(display.display, screen_id, *modes.offset(best_mode as isize)); + (display.xf86vmode.XF86VidModeSwitchToMode)(display.display, screen_id, &mut mode_to_switch_to); (display.xf86vmode.XF86VidModeSetViewPort)(display.display, screen_id, 0, 0); set_win_attr.override_redirect = 1; } @@ -551,6 +600,7 @@ impl Window { screen_id: screen_id, is_fullscreen: is_fullscreen, xf86_desk_mode: xf86_desk_mode, + colormap: cmap, }), is_closed: AtomicBool::new(false), wm_delete_window: wm_delete_window, @@ -564,11 +614,6 @@ impl Window { Ok(window) } - pub fn is_closed(&self) -> bool { - use std::sync::atomic::Ordering::Relaxed; - self.is_closed.load(Relaxed) - } - pub fn set_title(&self, title: &str) { with_c_str(title, |title| unsafe { (self.x.display.xlib.XStoreName)(self.x.display.display, self.x.window, title); @@ -768,11 +813,11 @@ impl Window { } impl GlContext for Window { - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { match self.x.context { Context::Glx(ref ctxt) => ctxt.make_current(), Context::Egl(ref ctxt) => ctxt.make_current(), - Context::None => {} + Context::None => Ok(()) } } @@ -792,11 +837,11 @@ impl GlContext for Window { } } - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { match self.x.context { Context::Glx(ref ctxt) => ctxt.swap_buffers(), Context::Egl(ref ctxt) => ctxt.swap_buffers(), - Context::None => {} + Context::None => Ok(()) } } diff --git a/src/events.rs b/src/events.rs index 6c4dc5b..5a5e2ab 100644 --- a/src/events.rs +++ b/src/events.rs @@ -25,11 +25,8 @@ pub enum Event { /// The parameter are the (x,y) coords in pixels relative to the top-left corner of the window. MouseMoved((i32, i32)), - /// Returns the horizontal and vertical mouse scrolling. - /// - /// A positive value indicates that the wheel was rotated forward, away from the user; - /// a negative value indicates that the wheel was rotated backward, toward the user. - MouseWheel(f64, f64), + /// A mouse wheel movement or touchpad scroll occurred. + MouseWheel(MouseScrollDelta), /// An event from the mouse has been received. MouseInput(ElementState, MouseButton), @@ -97,6 +94,23 @@ pub enum MouseButton { Other(u8), } +#[derive(Debug, Clone, Copy)] +pub enum MouseScrollDelta { + /// Amount in lines or rows to scroll in the horizontal + /// and vertical directions. + /// + /// Positive values indicate movement forward + /// (away from the user) or rightwards. + LineDelta(f32, f32), + /// Amount in pixels to scroll in the horizontal and + /// vertical direction. + /// + /// Scroll events are expressed as a PixelDelta if + /// supported by the device (eg. a touchpad) and + /// platform. + PixelDelta(f32, f32) +} + #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] pub enum VirtualKeyCode { /// The '1' key over the letters. diff --git a/src/headless.rs b/src/headless.rs index 997d87a..69f9aab 100644 --- a/src/headless.rs +++ b/src/headless.rs @@ -1,9 +1,11 @@ use Api; use BuilderAttribs; +use ContextError; use CreationError; use GlRequest; use GlContext; use PixelFormat; +use Robustness; use gl_common; use libc; @@ -42,6 +44,12 @@ impl HeadlessRendererBuilder { self } + /// Sets the robustness of the OpenGL context. See the docs of `Robustness`. + pub fn with_gl_robustness(mut self, robustness: Robustness) -> HeadlessRendererBuilder { + self.attribs.gl_robustness = robustness; + self + } + /// Builds the headless context. /// /// Error should be very rare and only occur in case of permission denied, incompatible system, @@ -69,7 +77,7 @@ impl HeadlessContext { /// Creates a new OpenGL context /// Sets the context as the current context. #[inline] - pub unsafe fn make_current(&self) { + pub unsafe fn make_current(&self) -> Result<(), ContextError> { self.context.make_current() } @@ -105,7 +113,7 @@ impl gl_common::GlFunctionsSource for HeadlessContext { } impl GlContext for HeadlessContext { - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { self.context.make_current() } @@ -117,7 +125,7 @@ impl GlContext for HeadlessContext { self.context.get_proc_address(addr) } - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { self.context.swap_buffers() } @@ -46,6 +46,8 @@ extern crate dwmapi; #[macro_use] extern crate objc; #[cfg(target_os = "macos")] +extern crate cgl; +#[cfg(target_os = "macos")] extern crate cocoa; #[cfg(target_os = "macos")] extern crate core_foundation; @@ -63,6 +65,8 @@ pub use window::{AvailableMonitorsIter, MonitorID, get_available_monitors, get_p #[cfg(feature = "window")] pub use native_monitor::NativeMonitorId; +use std::io; + mod api; mod platform; mod events; @@ -73,7 +77,7 @@ mod window; /// Trait that describes objects that have access to an OpenGL context. pub trait GlContext { /// Sets the context as the current context. - unsafe fn make_current(&self); + unsafe fn make_current(&self) -> Result<(), ContextError>; /// Returns true if this context is the current one in this thread. fn is_current(&self) -> bool; @@ -84,12 +88,12 @@ pub trait GlContext { /// Swaps the buffers in case of double or triple buffering. /// /// You should call this function every time you have finished rendering, or the image - /// may not be displayed on the screen. + /// may not be displayed on the screen. /// /// **Warning**: if you enabled vsync, this function will block until the next time the screen /// is refreshed. However drivers can choose to override your vsync settings, which means that /// you can't know in advance whether `swap_buffers` will block or not. - fn swap_buffers(&self); + fn swap_buffers(&self) -> Result<(), ContextError>; /// Returns the OpenGL API being used. fn get_api(&self) -> Api; @@ -126,6 +130,13 @@ impl std::error::Error for CreationError { } } +/// Error that can happen when manipulating an OpenGL context. +#[derive(Debug)] +pub enum ContextError { + IoError(io::Error), + ContextLost, +} + /// All APIs related to OpenGL that you can possibly get while using glutin. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Api { @@ -186,6 +197,40 @@ impl GlRequest { /// the compatibility profile features. pub static GL_CORE: GlRequest = GlRequest::Specific(Api::OpenGl, (3, 2)); +/// Specifies the tolerance of the OpenGL context to faults. If you accept raw OpenGL commands +/// and/or raw shader code from an untrusted source, you should definitely care about this. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Robustness { + /// Not everything is checked. Your application can crash if you do something wrong with your + /// shaders. + NotRobust, + + /// The driver doesn't check anything. This option is very dangerous. Please know what you're + /// doing before using it. See the `GL_KHR_no_error` extension. + /// + /// Since this option is purely an optimisation, no error will be returned if the backend + /// doesn't support it. Instead it will automatically fall back to `NotRobust`. + NoError, + + /// Everything is checked to avoid any crash. The driver will attempt to avoid any problem, + /// but if a problem occurs the behavior is implementation-defined. You are just guaranteed not + /// to get a crash. + RobustNoResetNotification, + + /// Same as `RobustNoResetNotification` but the context creation doesn't fail if it's not + /// supported. + TryRobustNoResetNotification, + + /// Everything is checked to avoid any crash. If a problem occurs, the context will enter a + /// "context lost" state. It must then be recreated. For the moment, glutin doesn't provide a + /// way to recreate a context with the same window :-/ + RobustLoseContextOnReset, + + /// Same as `RobustLoseContextOnReset` but the context creation doesn't fail if it's not + /// supported. + TryRobustLoseContextOnReset, +} + #[derive(Debug, Copy, Clone)] pub enum MouseCursor { /// The platform-dependent default cursor. @@ -288,6 +333,7 @@ pub struct BuilderAttribs<'a> { gl_version: GlRequest, gl_profile: Option<GlProfile>, gl_debug: bool, + gl_robustness: Robustness, vsync: bool, visible: bool, multisampling: Option<u16>, @@ -314,6 +360,7 @@ impl BuilderAttribs<'static> { gl_version: GlRequest::Latest, gl_profile: None, gl_debug: cfg!(debug_assertions), + gl_robustness: Robustness::NotRobust, vsync: false, visible: true, multisampling: None, @@ -345,6 +392,7 @@ impl<'a> BuilderAttribs<'a> { gl_version: self.gl_version, gl_profile: self.gl_profile, gl_debug: self.gl_debug, + gl_robustness: self.gl_robustness, vsync: self.vsync, visible: self.visible, multisampling: self.multisampling, @@ -390,8 +438,15 @@ impl<'a> BuilderAttribs<'a> { continue; } - if self.multisampling.is_some() && format.multisampling.is_none() { - continue; + if let Some(req_ms) = self.multisampling { + match format.multisampling { + Some(val) if val >= req_ms => (), + _ => continue + } + } else { + if format.multisampling.is_some() { + continue; + } } if let Some(srgb) = self.srgb { diff --git a/src/platform/android/mod.rs b/src/platform/android/mod.rs index a1b9416..50f554b 100644 --- a/src/platform/android/mod.rs +++ b/src/platform/android/mod.rs @@ -2,6 +2,8 @@ pub use api::android::*; +use ContextError; + pub struct HeadlessContext(i32); impl HeadlessContext { @@ -11,7 +13,7 @@ impl HeadlessContext { } /// See the docs in the crate root file. - pub unsafe fn make_current(&self) { + pub unsafe fn make_current(&self) -> Result<(), ContextError> { unimplemented!() } diff --git a/src/platform/emscripten/mod.rs b/src/platform/emscripten/mod.rs index 56a7e9f..d3aa6d6 100644 --- a/src/platform/emscripten/mod.rs +++ b/src/platform/emscripten/mod.rs @@ -1,5 +1,6 @@ #![cfg(target_os = "emscripten")] +use ContextError; use GlContext; pub use api::emscripten::{Window, WindowProxy, MonitorID, get_available_monitors}; @@ -15,7 +16,7 @@ impl HeadlessContext { } impl GlContext for HeadlessContext { - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { self.0.make_current() } @@ -27,7 +28,7 @@ impl GlContext for HeadlessContext { self.0.get_proc_address(addr) } - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { self.0.swap_buffers() } diff --git a/src/platform/linux/api_dispatch.rs b/src/platform/linux/api_dispatch.rs index d04a1fb..b2cd741 100644 --- a/src/platform/linux/api_dispatch.rs +++ b/src/platform/linux/api_dispatch.rs @@ -7,6 +7,7 @@ use std::collections::VecDeque; use std::sync::Arc; use BuilderAttribs; +use ContextError; use CreationError; use CursorState; use Event; @@ -154,13 +155,6 @@ impl Window { } } - pub fn is_closed(&self) -> bool { - match self { - &Window::X(ref w) => w.is_closed(), - &Window::Wayland(ref w) => w.is_closed() - } - } - pub fn set_title(&self, title: &str) { match self { &Window::X(ref w) => w.set_title(title), @@ -289,7 +283,7 @@ impl Window { } impl GlContext for Window { - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { match self { &Window::X(ref w) => w.make_current(), &Window::Wayland(ref w) => w.make_current() @@ -310,7 +304,7 @@ impl GlContext for Window { } } - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { match self { &Window::X(ref w) => w.swap_buffers(), &Window::Wayland(ref w) => w.swap_buffers() diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 323a21a..fc42f18 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -2,6 +2,7 @@ use Api; use BuilderAttribs; +use ContextError; use CreationError; use GlContext; use PixelFormat; @@ -37,7 +38,7 @@ impl HeadlessContext { impl GlContext for HeadlessContext { #[inline] - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { self.0.make_current() } @@ -52,7 +53,7 @@ impl GlContext for HeadlessContext { } #[inline] - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { self.0.swap_buffers() } diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs index 260ab94..b33b8ee 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform/windows/mod.rs @@ -6,6 +6,7 @@ use libc; use Api; use BuilderAttribs; +use ContextError; use CreationError; use PixelFormat; use GlContext; @@ -21,7 +22,7 @@ impl HeadlessContext { } impl GlContext for HeadlessContext { - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { self.0.make_current() } @@ -33,7 +34,7 @@ impl GlContext for HeadlessContext { self.0.get_proc_address(addr) } - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { self.0.swap_buffers() } diff --git a/src/window.rs b/src/window.rs index 664868b..8757efc 100644 --- a/src/window.rs +++ b/src/window.rs @@ -3,6 +3,7 @@ use std::default::Default; use Api; use BuilderAttribs; +use ContextError; use CreationError; use CursorState; use Event; @@ -11,6 +12,7 @@ use GlProfile; use GlRequest; use MouseCursor; use PixelFormat; +use Robustness; use native_monitor::NativeMonitorId; use gl_common; @@ -83,6 +85,12 @@ impl<'a> WindowBuilder<'a> { self } + /// Sets the robustness of the OpenGL context. See the docs of `Robustness`. + pub fn with_gl_robustness(mut self, robustness: Robustness) -> WindowBuilder<'a> { + self.attribs.gl_robustness = robustness; + self + } + /// Requests that the window has vsync enabled. pub fn with_vsync(mut self) -> WindowBuilder<'a> { self.attribs.vsync = true; @@ -230,12 +238,6 @@ impl Window { builder.build() } - /// Returns true if the window has previously been closed by the user. - #[inline] - pub fn is_closed(&self) -> bool { - self.window.is_closed() - } - /// Modifies the title of the window. /// /// This is a no-op if the window has already been closed. @@ -345,7 +347,7 @@ impl Window { /// Sets the context as the current context. #[inline] - pub unsafe fn make_current(&self) { + pub unsafe fn make_current(&self) -> Result<(), ContextError> { self.window.make_current() } @@ -372,7 +374,7 @@ impl Window { /// is refreshed. However drivers can choose to override your vsync settings, which means that /// you can't know in advance whether `swap_buffers` will block or not. #[inline] - pub fn swap_buffers(&self) { + pub fn swap_buffers(&self) -> Result<(), ContextError> { self.window.swap_buffers() } @@ -455,7 +457,7 @@ impl gl_common::GlFunctionsSource for Window { } impl GlContext for Window { - unsafe fn make_current(&self) { + unsafe fn make_current(&self) -> Result<(), ContextError> { self.make_current() } @@ -467,7 +469,7 @@ impl GlContext for Window { self.get_proc_address(addr) } - fn swap_buffers(&self) { + fn swap_buffers(&self) -> Result<(), ContextError> { self.swap_buffers() } |