diff options
Diffstat (limited to 'src/api')
-rw-r--r-- | src/api/cocoa/mod.rs | 8 | ||||
-rw-r--r-- | src/api/egl/mod.rs | 97 | ||||
-rw-r--r-- | src/api/glx/mod.rs | 82 | ||||
-rw-r--r-- | src/api/ios/delegate.rs | 226 | ||||
-rw-r--r-- | src/api/ios/ffi.rs | 130 | ||||
-rw-r--r-- | src/api/ios/mod.rs | 420 | ||||
-rw-r--r-- | src/api/mod.rs | 1 | ||||
-rw-r--r-- | src/api/osmesa/mod.rs | 8 | ||||
-rw-r--r-- | src/api/wayland/context.rs | 34 | ||||
-rw-r--r-- | src/api/wayland/mod.rs | 4 | ||||
-rw-r--r-- | src/api/wgl/mod.rs | 53 | ||||
-rw-r--r-- | src/api/x11/window.rs | 46 |
12 files changed, 1037 insertions, 72 deletions
diff --git a/src/api/cocoa/mod.rs b/src/api/cocoa/mod.rs index 772b906..332244e 100644 --- a/src/api/cocoa/mod.rs +++ b/src/api/cocoa/mod.rs @@ -13,6 +13,7 @@ use GlContext; use GlProfile; use GlRequest; use PixelFormat; +use Robustness; use native_monitor::NativeMonitorId; use objc::runtime::{Class, Object, Sel, BOOL, YES, NO}; @@ -332,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"))); }, diff --git a/src/api/egl/mod.rs b/src/api/egl/mod.rs index 7b31017..1a6cc06 100644 --- a/src/api/egl/mod.rs +++ b/src/api/egl/mod.rs @@ -7,6 +7,7 @@ use CreationError; use GlContext; use GlRequest; use PixelFormat; +use Robustness; use Api; use libc; @@ -116,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 { @@ -133,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 { @@ -335,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)); @@ -345,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 ") || @@ -356,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); } @@ -377,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/glx/mod.rs b/src/api/glx/mod.rs index 455f6a5..d286813 100644 --- a/src/api/glx/mod.rs +++ b/src/api/glx/mod.rs @@ -8,9 +8,10 @@ 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; @@ -48,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| { @@ -58,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)) }, }; @@ -191,9 +202,9 @@ impl Drop for 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> @@ -219,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/ios/delegate.rs b/src/api/ios/delegate.rs new file mode 100644 index 0000000..62cd89d --- /dev/null +++ b/src/api/ios/delegate.rs @@ -0,0 +1,226 @@ +use libc; +use std::mem; +use super::DelegateState; +use Event; +use events::{ Touch, TouchPhase }; + +use objc::runtime::{ Class, Object, Sel, BOOL, YES }; +use objc::declare::{ ClassDecl }; + +use super::ffi::{ + longjmp, + id, + nil, + CGRect, + CGPoint, + CGFloat, + UIViewAutoresizingFlexibleWidth, + UIViewAutoresizingFlexibleHeight + }; + +use super::jmpbuf; + + +pub fn create_delegate_class() { + extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL { + unsafe { + let main_screen: id = msg_send![Class::get("UIScreen").unwrap(), mainScreen]; + let bounds: CGRect = msg_send![main_screen, bounds]; + let scale: CGFloat = msg_send![main_screen, nativeScale]; + + let window: id = msg_send![Class::get("UIWindow").unwrap(), alloc]; + let window: id = msg_send![window, initWithFrame:bounds.clone()]; + + let size = (bounds.size.width as u32, bounds.size.height as u32); + + let view_controller: id = msg_send![Class::get("MainViewController").unwrap(), alloc]; + let view_controller: id = msg_send![view_controller, init]; + + + let class = Class::get("MainView").unwrap(); + let view:id = msg_send![class, alloc]; + let view:id = msg_send![view, initForGl:&bounds]; + + + let _: () = msg_send![view_controller, setView:view]; + + + let _: () = msg_send![window, setRootViewController:view_controller]; + + let _: () = msg_send![window, addSubview:view]; + let _: () = msg_send![window, makeKeyAndVisible]; + + let state = Box::new(DelegateState::new(window, view_controller, view, size, scale as f32)); + let state_ptr: *mut DelegateState = mem::transmute(state); + this.set_ivar("glutinState", state_ptr as *mut libc::c_void); + + + let _: () = msg_send![this, performSelector:sel!(postLaunch:) withObject:nil afterDelay:0.0]; + } + YES + } + + extern fn post_launch(_: &Object, _: Sel, _: id) { + unsafe { longjmp(mem::transmute(&mut jmpbuf),1); } + } + + extern fn did_become_active(this: &Object, _: Sel, _: id) { + unsafe { + let state: *mut libc::c_void = *this.get_ivar("glutinState"); + let state = &mut *(state as *mut DelegateState); + state.events_queue.push_back(Event::Focused(true)); + } + } + + extern fn will_resign_active(this: &Object, _: Sel, _: id) { + unsafe { + let state: *mut libc::c_void = *this.get_ivar("glutinState"); + let state = &mut *(state as *mut DelegateState); + state.events_queue.push_back(Event::Focused(false)); + } + } + + extern fn will_enter_foreground(this: &Object, _: Sel, _: id) { + unsafe { + let state: *mut libc::c_void = *this.get_ivar("glutinState"); + let state = &mut *(state as *mut DelegateState); + state.events_queue.push_back(Event::Suspended(false)); + } + } + + extern fn did_enter_background(this: &Object, _: Sel, _: id) { + unsafe { + let state: *mut libc::c_void = *this.get_ivar("glutinState"); + let state = &mut *(state as *mut DelegateState); + state.events_queue.push_back(Event::Suspended(true)); + } + } + + extern fn will_terminate(this: &Object, _: Sel, _: id) { + unsafe { + let state: *mut libc::c_void = *this.get_ivar("glutinState"); + let state = &mut *(state as *mut DelegateState); + // push event to the front to garantee that we'll process it + // immidiatly after jump + state.events_queue.push_front(Event::Closed); + longjmp(mem::transmute(&mut jmpbuf),1); + } + } + + extern fn handle_touches(this: &Object, _: Sel, touches: id, _:id) { + unsafe { + let state: *mut libc::c_void = *this.get_ivar("glutinState"); + let state = &mut *(state as *mut DelegateState); + + let touches_enum: id = msg_send![touches, objectEnumerator]; + + loop { + let touch: id = msg_send![touches_enum, nextObject]; + if touch == nil { + break + } + let location: CGPoint = msg_send![touch, locationInView:nil]; + let touch_id = touch as u64; + let phase: i32 = msg_send![touch, phase]; + + state.events_queue.push_back(Event::Touch(Touch { + id: touch_id, + location: (location.x as f64, location.y as f64), + phase: match phase { + 0 => TouchPhase::Started, + 1 => TouchPhase::Moved, + // 2 is UITouchPhaseStationary and is not expected here + 3 => TouchPhase::Ended, + 4 => TouchPhase::Cancelled, + _ => panic!("unexpected touch phase: {:?}", phase) + } + })); + } + } + } + + let superclass = Class::get("UIResponder").unwrap(); + let mut decl = ClassDecl::new(superclass, "AppDelegate").unwrap(); + + unsafe { + decl.add_method(sel!(application:didFinishLaunchingWithOptions:), + did_finish_launching as extern fn(&mut Object, Sel, id, id) -> BOOL); + + decl.add_method(sel!(applicationDidBecomeActive:), + did_become_active as extern fn(&Object, Sel, id)); + + decl.add_method(sel!(applicationWillResignActive:), + will_resign_active as extern fn(&Object, Sel, id)); + + decl.add_method(sel!(applicationWillEnterForeground:), + will_enter_foreground as extern fn(&Object, Sel, id)); + + decl.add_method(sel!(applicationDidEnterBackground:), + did_enter_background as extern fn(&Object, Sel, id)); + + decl.add_method(sel!(applicationWillTerminate:), + will_terminate as extern fn(&Object, Sel, id)); + + + decl.add_method(sel!(touchesBegan:withEvent:), + handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id)); + + decl.add_method(sel!(touchesMoved:withEvent:), + handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id)); + + decl.add_method(sel!(touchesEnded:withEvent:), + handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id)); + + decl.add_method(sel!(touchesCancelled:withEvent:), + handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id)); + + + + decl.add_method(sel!(postLaunch:), + post_launch as extern fn(&Object, Sel, id)); + + decl.add_ivar::<*mut libc::c_void>("glutinState"); + + decl.register(); + } +} + + +pub fn create_view_class() { + let superclass = Class::get("UIViewController").unwrap(); + let decl = ClassDecl::new(superclass, "MainViewController").unwrap(); + + decl.register(); + + extern fn init_for_gl(this: &Object, _: Sel, frame: *const libc::c_void) -> id { + unsafe { + let bounds: *const CGRect = mem::transmute(frame); + let view: id = msg_send![this, initWithFrame:(*bounds).clone()]; + + let _: () = msg_send![view, setAutoresizingMask: UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight]; + let _: () = msg_send![view, setAutoresizesSubviews:YES]; + + let layer: id = msg_send![view, layer]; + let _ : () = msg_send![layer, setOpaque:YES]; + + view + } + } + + extern fn layer_class(_: &Class, _: Sel) -> *const Class { + unsafe { mem::transmute(Class::get("CAEAGLLayer").unwrap()) } + } + + + let superclass = Class::get("UIView").unwrap(); + let mut decl = ClassDecl::new(superclass, "MainView").unwrap(); + + unsafe { + decl.add_method(sel!(initForGl:), + init_for_gl as extern fn(&Object, Sel, *const libc::c_void) -> id); + + decl.add_class_method(sel!(layerClass), + layer_class as extern fn(&Class, Sel) -> *const Class); + decl.register(); + } +}
\ No newline at end of file diff --git a/src/api/ios/ffi.rs b/src/api/ios/ffi.rs new file mode 100644 index 0000000..72f2ff2 --- /dev/null +++ b/src/api/ios/ffi.rs @@ -0,0 +1,130 @@ +use std::ffi::CString; + +use libc; +use objc::runtime::{ Object, Class }; + +#[allow(non_camel_case_types)] +pub type id = *mut Object; + +#[allow(non_camel_case_types)] +#[allow(non_upper_case_globals)] +pub const nil: id = 0 as id; + +pub type CFStringRef = *const libc::c_void; +pub type CFTimeInterval = f64; +pub type Boolean = u32; + +#[allow(non_upper_case_globals)] +pub const kCFRunLoopRunHandledSource: i32 = 4; + +#[cfg(target_pointer_width = "32")] +pub type CGFloat = f32; +#[cfg(target_pointer_width = "64")] +pub type CGFloat = f64; + +#[cfg(target_pointer_width = "32")] +pub type NSUInteger = u32; +#[cfg(target_pointer_width = "64")] +pub type NSUInteger = u64; + +#[allow(non_upper_case_globals)] +pub const UIViewAutoresizingFlexibleWidth: NSUInteger = 1 << 1; +#[allow(non_upper_case_globals)] +pub const UIViewAutoresizingFlexibleHeight: NSUInteger = 1 << 4; + + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct CGPoint { + pub x: CGFloat, + pub y: CGFloat, +} + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct CGRect { + pub origin: CGPoint, + pub size: CGSize +} + +#[repr(C)] +#[derive(Debug, Clone)] +pub struct CGSize { + pub width: CGFloat, + pub height: CGFloat +} + +pub mod gles { + include!(concat!(env!("OUT_DIR"), "/gles2_bindings.rs")); +} + +#[link(name = "UIKit", kind = "framework")] +#[link(name = "CoreFoundation", kind = "framework")] +#[link(name = "GlKit", kind = "framework")] +extern { + pub static kCFRunLoopDefaultMode: CFStringRef; + + pub static kEAGLColorFormatRGB565: id; + // pub static kEAGLColorFormatRGBA8: id; + pub static kEAGLDrawablePropertyColorFormat: id; + pub static kEAGLDrawablePropertyRetainedBacking: id; + + // int UIApplicationMain ( int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName ); + pub fn UIApplicationMain(argc: libc::c_int, argv: *const libc::c_char, principalClassName: id, delegateClassName: id) -> libc::c_int; + + // SInt32 CFRunLoopRunInMode ( CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled ); + pub fn CFRunLoopRunInMode(mode: CFStringRef, seconds: CFTimeInterval, returnAfterSourceHandled: Boolean) -> i32; +} + +extern { + pub fn setjmp(env: *mut libc::c_void) -> libc::c_int; + pub fn longjmp(env: *mut libc::c_void, val: libc::c_int); +} + +pub const RTLD_LAZY: libc::c_int = 0x001; +pub const RTLD_GLOBAL: libc::c_int = 0x100; + +extern { + pub fn dlopen(filename: *const libc::c_char, flag: libc::c_int) -> *mut libc::c_void; + pub fn dlsym(handle: *mut libc::c_void, symbol: *const libc::c_char) -> *mut libc::c_void; +} + +pub trait NSString { + unsafe fn alloc(_: Self) -> id { + msg_send![class("NSString"), alloc] + } + + #[allow(non_snake_case)] + unsafe fn initWithUTF8String_(self, c_string: *const i8) -> id; + #[allow(non_snake_case)] + unsafe fn stringByAppendingString_(self, other: id) -> id; + unsafe fn init_str(self, string: &str) -> Self; + #[allow(non_snake_case)] + unsafe fn UTF8String(self) -> *const libc::c_char; +} + +impl NSString for id { + unsafe fn initWithUTF8String_(self, c_string: *const i8) -> id { + msg_send![self, initWithUTF8String:c_string as id] + } + + unsafe fn stringByAppendingString_(self, other: id) -> id { + msg_send![self, stringByAppendingString:other] + } + + unsafe fn init_str(self, string: &str) -> id { + let cstring = CString::new(string).unwrap(); + self.initWithUTF8String_(cstring.as_ptr()) + } + + unsafe fn UTF8String(self) -> *const libc::c_char { + msg_send![self, UTF8String] + } +} + +#[inline] +pub fn class(name: &str) -> *mut Class { + unsafe { + ::std::mem::transmute(Class::get(name)) + } +} diff --git a/src/api/ios/mod.rs b/src/api/ios/mod.rs new file mode 100644 index 0000000..9d1b527 --- /dev/null +++ b/src/api/ios/mod.rs @@ -0,0 +1,420 @@ +//! iOS support +//! +//! # Building app +//! To build ios app you will need rustc built for this targets: +//! +//! - armv7-apple-ios +//! - armv7s-apple-ios +//! - i386-apple-ios +//! - aarch64-apple-ios +//! - x86_64-apple-ios +//! +//! Then +//! +//! ``` +//! cargo build --target=... +//! ``` +//! The simplest way to integrate your app into xcode environment is to build it +//! as a static library. Wrap your main function and export it. +//! +//! ```rust, ignore +//! #[no_mangle] +//! pub extern fn start_glutin_app() { +//! start_inner() +//! } +//! +//! fn start_inner() { +//! ... +//! } +//! +//! ``` +//! +//! Compile project and then drag resulting .a into Xcode project. Add glutin.h to xcode. +//! +//! ```c +//! void start_glutin_app(); +//! ``` +//! +//! Use start_glutin_app inside your xcode's main function. +//! +//! +//! # App lifecycle and events +//! +//! iOS environment is very different from other platforms and you must be very +//! careful with it's events. Familiarize yourself with [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/). +//! +//! +//! This is how those event are represented in glutin: +//! +//! - applicationDidBecomeActive is Focused(true) +//! - applicationWillResignActive is Focused(false) +//! - applicationDidEnterBackground is Suspended(true) +//! - applicationWillEnterForeground is Suspended(false) +//! - applicationWillTerminate is Closed +//! +//! Keep in mind that after Closed event is received every attempt to draw with opengl will result in segfault. +//! +//! Also note that app will not receive Closed event if suspended, it will be SIGKILL'ed + + + + +#![cfg(target_os = "ios")] +#![deny(warnings)] + +use std::collections::VecDeque; +use std::ptr; +use std::mem; +use std::ffi::CString; + +use libc; +use objc::runtime::{Class, BOOL, YES, NO }; + +use native_monitor::NativeMonitorId; +use { Api, PixelFormat, CreationError, BuilderAttribs, GlContext, CursorState, MouseCursor, Event }; +use CreationError::OsError; + +mod delegate; +use self::delegate::{ create_delegate_class, create_view_class }; + +mod ffi; +use self::ffi::{ + gles, + setjmp, + dlopen, + dlsym, + UIApplicationMain, + kEAGLColorFormatRGB565, + CFTimeInterval, + CFRunLoopRunInMode, + kCFRunLoopDefaultMode, + kCFRunLoopRunHandledSource, + kEAGLDrawablePropertyRetainedBacking, + kEAGLDrawablePropertyColorFormat, + RTLD_LAZY, + RTLD_GLOBAL, + id, + nil, + NSString, + CGFloat + }; + + +static mut jmpbuf: [libc::c_int;27] = [0;27]; + +pub struct MonitorID; + +pub struct Window { + eagl_context: id, + delegate_state: *mut DelegateState +} + +#[derive(Clone)] +pub struct WindowProxy; + +pub struct PollEventsIterator<'a> { + window: &'a Window, +} + +pub struct WaitEventsIterator<'a> { + window: &'a Window, +} + +#[derive(Debug)] +struct DelegateState { + events_queue: VecDeque<Event>, + window: id, + controller: id, + view: id, + size: (u32,u32), + scale: f32 +} + + +impl DelegateState { + fn new(window: id, controller:id, view: id, size: (u32,u32), scale: f32) -> DelegateState { + DelegateState { + events_queue: VecDeque::new(), + window: window, + controller: controller, + view: view, + size: size, + scale: scale + } + } +} + + +pub fn get_available_monitors() -> VecDeque<MonitorID> { + let mut rb = VecDeque::new(); + rb.push_back(MonitorID); + rb +} + +pub fn get_primary_monitor() -> MonitorID { + MonitorID +} + +impl MonitorID { + pub fn get_name(&self) -> Option<String> { + Some("Primary".to_string()) + } + + pub fn get_native_identifier(&self) -> NativeMonitorId { + NativeMonitorId::Unavailable + } + + pub fn get_dimensions(&self) -> (u32, u32) { + unimplemented!() + } +} + + +impl Window { + + pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> { + unsafe { + if setjmp(mem::transmute(&mut jmpbuf)) != 0 { + let app: id = msg_send![Class::get("UIApplication").unwrap(), sharedApplication]; + let delegate: id = msg_send![app, delegate]; + let state: *mut libc::c_void = *(&*delegate).get_ivar("glutinState"); + let state = state as *mut DelegateState; + + let context = Window::create_context(); + + let mut window = Window { + eagl_context: context, + delegate_state: state + }; + + window.init_context(builder); + + return Ok(window) + } + } + + create_delegate_class(); + create_view_class(); + Window::start_app(); + + Err(CreationError::OsError(format!("Couldn't create UIApplication"))) + } + + unsafe fn init_context(&mut self, builder: BuilderAttribs) { + let draw_props: id = msg_send![Class::get("NSDictionary").unwrap(), alloc]; + let draw_props: id = msg_send![draw_props, + initWithObjects: + vec![ + msg_send![Class::get("NSNumber").unwrap(), numberWithBool: NO], + kEAGLColorFormatRGB565 + ].as_ptr() + forKeys: + vec![ + kEAGLDrawablePropertyRetainedBacking, + kEAGLDrawablePropertyColorFormat + ].as_ptr() + count: 2 + ]; + self.make_current(); + + let state = &mut *self.delegate_state; + + if builder.multitouch { + let _: () = msg_send![state.view, setMultipleTouchEnabled:YES]; + } + + let _: () = msg_send![state.view, setContentScaleFactor:state.scale as CGFloat]; + + let layer: id = msg_send![state.view, layer]; + let _: () = msg_send![layer, setContentsScale:state.scale as CGFloat]; + let _: () = msg_send![layer, setDrawableProperties: draw_props]; + + let gl = gles::Gles2::load_with(|symbol| self.get_proc_address(symbol)); + let mut color_render_buf: gles::types::GLuint = 0; + let mut frame_buf: gles::types::GLuint = 0; + gl.GenRenderbuffers(1, &mut color_render_buf); + gl.BindRenderbuffer(gles::RENDERBUFFER, color_render_buf); + + let ok: BOOL = msg_send![self.eagl_context, renderbufferStorage:gles::RENDERBUFFER fromDrawable:layer]; + if ok != YES { + panic!("EAGL: could not set renderbufferStorage"); + } + + gl.GenFramebuffers(1, &mut frame_buf); + gl.BindFramebuffer(gles::FRAMEBUFFER, frame_buf); + + gl.FramebufferRenderbuffer(gles::FRAMEBUFFER, gles::COLOR_ATTACHMENT0, gles::RENDERBUFFER, color_render_buf); + + let status = gl.CheckFramebufferStatus(gles::FRAMEBUFFER); + if gl.CheckFramebufferStatus(gles::FRAMEBUFFER) != gles::FRAMEBUFFER_COMPLETE { + panic!("framebuffer status: {:?}", status); + } + } + + fn create_context() -> id { + unsafe { + let eagl_context: id = msg_send![Class::get("EAGLContext").unwrap(), alloc]; + let eagl_context: id = msg_send![eagl_context, initWithAPI:2]; // es2 + eagl_context + } + } + + fn start_app() { + unsafe { + UIApplicationMain(0, ptr::null(), nil, NSString::alloc(nil).init_str("AppDelegate")); + } + } + + pub fn is_closed(&self) -> bool { + false + } + + pub fn set_title(&self, _: &str) { + } + + pub fn show(&self) { + } + + pub fn hide(&self) { + } + + pub fn get_position(&self) -> Option<(i32, i32)> { + None + } + + pub fn set_position(&self, _x: i32, _y: i32) { + } + + pub fn get_inner_size(&self) -> Option<(u32, u32)> { + unsafe { Some((&*self.delegate_state).size) } + } + + pub fn get_outer_size(&self) -> Option<(u32, u32)> { + self.get_inner_size() + } + + pub fn set_inner_size(&self, _x: u32, _y: u32) { + } + + pub fn poll_events(&self) -> PollEventsIterator { + PollEventsIterator { + window: self + } + } + + pub fn wait_events(&self) -> WaitEventsIterator { + WaitEventsIterator { + window: self + } + } + + pub fn platform_display(&self) -> *mut libc::c_void { + unimplemented!(); + } + + pub fn platform_window(&self) -> *mut libc::c_void { + unimplemented!() + } + + pub fn get_pixel_format(&self) -> PixelFormat { + unimplemented!(); + } + + pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) { + } + + pub fn set_cursor(&self, _: MouseCursor) { + } + + pub fn set_cursor_state(&self, _: CursorState) -> Result<(), String> { + Ok(()) + } + + pub fn hidpi_factor(&self) -> f32 { + unsafe { (&*self.delegate_state) }.scale + } + + pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> { + unimplemented!(); + } + + pub fn create_window_proxy(&self) -> WindowProxy { + WindowProxy + } + +} + +impl GlContext for Window { + unsafe fn make_current(&self) { + let _:BOOL = msg_send![Class::get("EAGLContext").unwrap(), setCurrentContext: self.eagl_context]; + } + + fn is_current(&self) -> bool { + false + } + + fn get_proc_address(&self, addr: &str) -> *const libc::c_void { + let addr_c = CString::new(addr).unwrap(); + let path = CString::new("/System/Library/Frameworks/OpenGLES.framework/OpenGLES").unwrap(); + unsafe { + let lib = dlopen(path.as_ptr(), RTLD_LAZY | RTLD_GLOBAL); + dlsym(lib, addr_c.as_ptr()) + } + } + + fn swap_buffers(&self) { + unsafe { let _:BOOL = msg_send![self.eagl_context, presentRenderbuffer: gles::RENDERBUFFER]; } + } + + fn get_api(&self) -> Api { + unimplemented!() + } + + fn get_pixel_format(&self) -> PixelFormat { + unimplemented!() + } +} + +impl WindowProxy { + pub fn wakeup_event_loop(&self) { + unimplemented!() + } +} + + +impl<'a> Iterator for WaitEventsIterator<'a> { + type Item = Event; + + fn next(&mut self) -> Option<Event> { + loop { + if let Some(ev) = self.window.poll_events().next() { + return Some(ev); + } + } + } +} + +impl<'a> Iterator for PollEventsIterator<'a> { + type Item = Event; + + fn next(&mut self) -> Option<Event> { + unsafe { + let state = &mut *self.window.delegate_state; + + if let Some(event) = state.events_queue.pop_front() { + return Some(event) + } + + // jump hack, so we won't quit on willTerminate event before processing it + if setjmp(mem::transmute(&mut jmpbuf)) != 0 { + return state.events_queue.pop_front() + } + + // run runloop + let seconds: CFTimeInterval = 0.000002; + while CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, 1) == kCFRunLoopRunHandledSource {} + + state.events_queue.pop_front() + } + } +} diff --git a/src/api/mod.rs b/src/api/mod.rs index db5590a..3697255 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -10,3 +10,4 @@ pub mod wayland; pub mod wgl; pub mod win32; pub mod x11; +pub mod ios; diff --git a/src/api/osmesa/mod.rs b/src/api/osmesa/mod.rs index daaba52..db0a1e7 100644 --- a/src/api/osmesa/mod.rs +++ b/src/api/osmesa/mod.rs @@ -8,6 +8,7 @@ use ContextError; use CreationError; use GlContext; use PixelFormat; +use Robustness; use libc; use std::{mem, ptr}; use std::ffi::CString; @@ -38,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, 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 852172b..0af5496 100644 --- a/src/api/wayland/mod.rs +++ b/src/api/wayland/mod.rs @@ -2,7 +2,9 @@ #![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; diff --git a/src/api/wgl/mod.rs b/src/api/wgl/mod.rs index c33054e..cf09ad0 100644 --- a/src/api/wgl/mod.rs +++ b/src/api/wgl/mod.rs @@ -7,6 +7,7 @@ use GlContext; use GlRequest; use GlProfile; use PixelFormat; +use Robustness; use Api; use self::make_current_guard::CurrentContextGuard; @@ -131,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 { @@ -181,11 +180,14 @@ impl GlContext for Context { } fn swap_buffers(&self) -> Result<(), ContextError> { - if unsafe { gdi32::SwapBuffers(self.hdc) } != 0 { + // 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 { @@ -266,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/x11/window.rs b/src/api/x11/window.rs index dc24e38..15e27ce 100644 --- a/src/api/x11/window.rs +++ b/src/api/x11/window.rs @@ -365,29 +365,44 @@ impl Window { 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 @@ -459,10 +474,11 @@ impl Window { window_attributes |= ffi::CWBackPixel; } - if builder.monitor.is_some() { + // 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; } |