diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b41a77f51..c694bd087a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Unreleased +- We no longer load `libegl.so` and `libgl.so` multiple times. +- Fixes `Context::is_current` incorrectly returning `false`. +- **Breaking:** Renamed `GlContext{,Ext}` to `ContextTrait{,Ext}`. +- Implemented context sharing support for Windows and Linux. +- Added `SeparatedContext`. +- **Breaking:** Renamed `GlWindow` to `CombinedContext`. +- **Breaking:** Removed `shareable_with_windowed_contexts`. Now you must build +OsMesa contexts via a separate extension. +- Added `ContextBuilder::build` method. +- On X11 and Wayland, you can now use shared contexts, however, one limitation +of the Wayland backend is that all shared contexts must use the same events +pool as each other. +- Added context sharing support to windows. +- Improved docs. +- Refactored code to be more consistent/cleaner. Ran rustfmt on everything. - Added NetBSD support. - **Breaking:** Removed `new_shared` function from `Context` and `GlWindow`, in favor of `new`. - Added `build` method to `ContextBuilder`. diff --git a/Cargo.toml b/Cargo.toml index 131f12dfa5..fd3045f0a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,10 +18,10 @@ icon_loading = ["winit/icon_loading"] serde = ["winit/serde"] [dependencies] -lazy_static = "1" +lazy_static = "1.1" libc = "0.2" -shared_library = "0.1.0" -winit = "0.18.0" +shared_library = "0.1" +winit = "0.18" [build-dependencies] gl_generator = "0.10" @@ -48,7 +48,11 @@ features = [ "libloaderapi", ] +[target.'cfg(target_os = "windows")'.dependencies] +libloading = "0.5" + [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os="dragonfly", target_os="netbsd", target_os="openbsd"))'.dependencies] osmesa-sys = "0.1.0" wayland-client = { version = "0.21", features = ["egl", "dlopen"] } x11-dl = "2.18.3" +libloading = "0.5" diff --git a/README.md b/README.md index 65cc052a1f..b860dbc41d 100644 --- a/README.md +++ b/README.md @@ -39,35 +39,36 @@ extern crate gl; extern crate glutin; use glutin::dpi::*; -use glutin::GlContext; +use glutin::ContextTrait; fn main() { - let mut events_loop = glutin::EventsLoop::new(); - let window = glutin::WindowBuilder::new() + let mut el = glutin::EventsLoop::new(); + let wb = glutin::WindowBuilder::new() .with_title("Hello, world!") .with_dimensions(LogicalSize::new(1024.0, 768.0)); - let context = glutin::ContextBuilder::new() - .with_vsync(true); - let gl_window = glutin::GlWindow::new(window, context, &events_loop).unwrap(); + let combined_context = glutin::ContextBuilder::new() + .with_vsync(true) + .build_combined(wb, &el) + .unwrap(); unsafe { - gl_window.make_current().unwrap(); + combined_context.make_current().unwrap(); } unsafe { - gl::load_with(|symbol| gl_window.get_proc_address(symbol) as *const _); + gl::load_with(|symbol| combined_context.get_proc_address(symbol) as *const _); gl::ClearColor(0.0, 1.0, 0.0, 1.0); } let mut running = true; while running { - events_loop.poll_events(|event| { + el.poll_events(|event| { match event { glutin::Event::WindowEvent{ event, .. } => match event { glutin::WindowEvent::CloseRequested => running = false, glutin::WindowEvent::Resized(logical_size) => { - let dpi_factor = gl_window.get_hidpi_factor(); - gl_window.resize(logical_size.to_physical(dpi_factor)); + let dpi_factor = combined_context.get_hidpi_factor(); + combined_context.resize(logical_size.to_physical(dpi_factor)); }, _ => () }, @@ -79,7 +80,7 @@ fn main() { gl::Clear(gl::COLOR_BUFFER_BIT); } - gl_window.swap_buffers().unwrap(); + combined_context.swap_buffers().unwrap(); } } ``` diff --git a/build.rs b/build.rs index 3d4b0b02c8..b9faa30a0e 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,6 @@ extern crate gl_generator; -use gl_generator::{Registry, Api, Profile, Fallbacks}; +use gl_generator::{Api, Fallbacks, Profile, Registry}; use std::env; use std::fs::File; use std::path::PathBuf; @@ -17,131 +17,190 @@ fn main() { .write_bindings(gl_generator::StaticGenerator, &mut file) .unwrap(); - let mut file = File::create(&dest.join("wgl_extra_bindings.rs")).unwrap(); - Registry::new(Api::Wgl, (1, 0), Profile::Core, Fallbacks::All, [ - "WGL_ARB_create_context", - "WGL_ARB_create_context_profile", - "WGL_ARB_create_context_robustness", - "WGL_ARB_context_flush_control", - "WGL_ARB_extensions_string", - "WGL_ARB_framebuffer_sRGB", - "WGL_ARB_multisample", - "WGL_ARB_pixel_format", - "WGL_ARB_pixel_format_float", - "WGL_EXT_create_context_es2_profile", - "WGL_EXT_extensions_string", - "WGL_EXT_framebuffer_sRGB", - "WGL_EXT_swap_control", - ]) - .write_bindings(gl_generator::StructGenerator, &mut file).unwrap(); + let mut file = + File::create(&dest.join("wgl_extra_bindings.rs")).unwrap(); + Registry::new( + Api::Wgl, + (1, 0), + Profile::Core, + Fallbacks::All, + [ + "WGL_ARB_create_context", + "WGL_ARB_create_context_profile", + "WGL_ARB_create_context_robustness", + "WGL_ARB_context_flush_control", + "WGL_ARB_extensions_string", + "WGL_ARB_framebuffer_sRGB", + "WGL_ARB_multisample", + "WGL_ARB_pixel_format", + "WGL_ARB_pixel_format_float", + "WGL_EXT_create_context_es2_profile", + "WGL_EXT_extensions_string", + "WGL_EXT_framebuffer_sRGB", + "WGL_EXT_swap_control", + ], + ) + .write_bindings(gl_generator::StructGenerator, &mut file) + .unwrap(); let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap(); - Registry::new(Api::Egl, (1, 5), Profile::Core, Fallbacks::All, [ - "EGL_KHR_create_context", - "EGL_EXT_create_context_robustness", - "EGL_KHR_create_context_no_error", - "EGL_KHR_platform_x11", - "EGL_KHR_platform_android", - "EGL_KHR_platform_wayland", - "EGL_KHR_platform_gbm", - "EGL_EXT_platform_base", - "EGL_EXT_platform_x11", - "EGL_MESA_platform_gbm", - "EGL_EXT_platform_wayland", - "EGL_EXT_platform_device", - ]) - .write_bindings(gl_generator::StructGenerator, &mut file).unwrap(); + Registry::new( + Api::Egl, + (1, 5), + Profile::Core, + Fallbacks::All, + [ + "EGL_KHR_create_context", + "EGL_EXT_create_context_robustness", + "EGL_KHR_create_context_no_error", + "EGL_KHR_platform_x11", + "EGL_KHR_platform_android", + "EGL_KHR_platform_wayland", + "EGL_KHR_platform_gbm", + "EGL_EXT_platform_base", + "EGL_EXT_platform_x11", + "EGL_MESA_platform_gbm", + "EGL_EXT_platform_wayland", + "EGL_EXT_platform_device", + ], + ) + .write_bindings(gl_generator::StructGenerator, &mut file) + .unwrap(); } - if target.contains("linux") || target.contains("dragonfly") || target.contains("freebsd") || target.contains("netbsd") || target.contains("openbsd") { + if target.contains("linux") + || target.contains("dragonfly") + || target.contains("freebsd") + || target.contains("netbsd") + || target.contains("openbsd") + { let mut file = File::create(&dest.join("glx_bindings.rs")).unwrap(); Registry::new(Api::Glx, (1, 4), Profile::Core, Fallbacks::All, []) - .write_bindings(gl_generator::StructGenerator, &mut file).unwrap(); + .write_bindings(gl_generator::StructGenerator, &mut file) + .unwrap(); - let mut file = File::create(&dest.join("glx_extra_bindings.rs")).unwrap(); - Registry::new(Api::Glx, (1, 4), Profile::Core, Fallbacks::All, [ - "GLX_ARB_create_context", - "GLX_ARB_create_context_profile", - "GLX_ARB_create_context_robustness", - "GLX_ARB_context_flush_control", - "GLX_ARB_fbconfig_float", - "GLX_ARB_framebuffer_sRGB", - "GLX_EXT_framebuffer_sRGB", - "GLX_ARB_multisample", - "GLX_EXT_swap_control", - "GLX_SGI_swap_control" - ]) - .write_bindings(gl_generator::StructGenerator, &mut file).unwrap(); + let mut file = + File::create(&dest.join("glx_extra_bindings.rs")).unwrap(); + Registry::new( + Api::Glx, + (1, 4), + Profile::Core, + Fallbacks::All, + [ + "GLX_ARB_create_context", + "GLX_ARB_create_context_profile", + "GLX_ARB_create_context_robustness", + "GLX_ARB_context_flush_control", + "GLX_ARB_fbconfig_float", + "GLX_ARB_framebuffer_sRGB", + "GLX_EXT_framebuffer_sRGB", + "GLX_ARB_multisample", + "GLX_EXT_swap_control", + "GLX_SGI_swap_control", + ], + ) + .write_bindings(gl_generator::StructGenerator, &mut file) + .unwrap(); let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap(); - Registry::new(Api::Egl, (1, 5), Profile::Core, Fallbacks::All, [ - "EGL_KHR_create_context", - "EGL_EXT_create_context_robustness", - "EGL_KHR_create_context_no_error", - "EGL_KHR_platform_x11", - "EGL_KHR_platform_android", - "EGL_KHR_platform_wayland", - "EGL_KHR_platform_gbm", - "EGL_EXT_platform_base", - "EGL_EXT_platform_x11", - "EGL_MESA_platform_gbm", - "EGL_EXT_platform_wayland", - "EGL_EXT_platform_device", - ]) - .write_bindings(gl_generator::StructGenerator, &mut file).unwrap(); + Registry::new( + Api::Egl, + (1, 5), + Profile::Core, + Fallbacks::All, + [ + "EGL_KHR_create_context", + "EGL_EXT_create_context_robustness", + "EGL_KHR_create_context_no_error", + "EGL_KHR_platform_x11", + "EGL_KHR_platform_android", + "EGL_KHR_platform_wayland", + "EGL_KHR_platform_gbm", + "EGL_EXT_platform_base", + "EGL_EXT_platform_x11", + "EGL_MESA_platform_gbm", + "EGL_EXT_platform_wayland", + "EGL_EXT_platform_device", + ], + ) + .write_bindings(gl_generator::StructGenerator, &mut file) + .unwrap(); } if target.contains("android") { let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap(); - Registry::new(Api::Egl, (1, 5), Profile::Core, Fallbacks::All, [ - "EGL_KHR_create_context", - "EGL_EXT_create_context_robustness", - "EGL_KHR_create_context_no_error", - "EGL_KHR_platform_x11", - "EGL_KHR_platform_android", - "EGL_KHR_platform_wayland", - "EGL_KHR_platform_gbm", - "EGL_EXT_platform_base", - "EGL_EXT_platform_x11", - "EGL_MESA_platform_gbm", - "EGL_EXT_platform_wayland", - "EGL_EXT_platform_device", - ]) - .write_bindings(gl_generator::StaticStructGenerator, &mut file).unwrap(); + Registry::new( + Api::Egl, + (1, 5), + Profile::Core, + Fallbacks::All, + [ + "EGL_KHR_create_context", + "EGL_EXT_create_context_robustness", + "EGL_KHR_create_context_no_error", + "EGL_KHR_platform_x11", + "EGL_KHR_platform_android", + "EGL_KHR_platform_wayland", + "EGL_KHR_platform_gbm", + "EGL_EXT_platform_base", + "EGL_EXT_platform_x11", + "EGL_MESA_platform_gbm", + "EGL_EXT_platform_wayland", + "EGL_EXT_platform_device", + ], + ) + .write_bindings(gl_generator::StaticStructGenerator, &mut file) + .unwrap(); } if target.contains("ios") { let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap(); - Registry::new(Api::Egl, (1, 5), Profile::Core, Fallbacks::All, [ - "EGL_KHR_create_context", - "EGL_EXT_create_context_robustness", - "EGL_KHR_create_context_no_error", - "EGL_KHR_platform_x11", - "EGL_KHR_platform_android", - "EGL_KHR_platform_wayland", - "EGL_KHR_platform_gbm", - "EGL_EXT_platform_base", - "EGL_EXT_platform_x11", - "EGL_MESA_platform_gbm", - "EGL_EXT_platform_wayland", - "EGL_EXT_platform_device", - ]) - .write_bindings(gl_generator::StaticStructGenerator, &mut file).unwrap(); + Registry::new( + Api::Egl, + (1, 5), + Profile::Core, + Fallbacks::All, + [ + "EGL_KHR_create_context", + "EGL_EXT_create_context_robustness", + "EGL_KHR_create_context_no_error", + "EGL_KHR_platform_x11", + "EGL_KHR_platform_android", + "EGL_KHR_platform_wayland", + "EGL_KHR_platform_gbm", + "EGL_EXT_platform_base", + "EGL_EXT_platform_x11", + "EGL_MESA_platform_gbm", + "EGL_EXT_platform_wayland", + "EGL_EXT_platform_device", + ], + ) + .write_bindings(gl_generator::StaticStructGenerator, &mut file) + .unwrap(); let mut file = File::create(&dest.join("gles2_bindings.rs")).unwrap(); Registry::new(Api::Gles2, (2, 0), Profile::Core, Fallbacks::None, []) - .write_bindings(gl_generator::StaticStructGenerator, &mut file).unwrap(); + .write_bindings(gl_generator::StaticStructGenerator, &mut file) + .unwrap(); } if target.contains("darwin") { let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap(); - Registry::new(Api::Gl, (3, 2), Profile::Core, Fallbacks::All, ["GL_EXT_framebuffer_object"]) - .write_bindings(gl_generator::GlobalGenerator, &mut file).unwrap(); + Registry::new( + Api::Gl, + (3, 2), + Profile::Core, + Fallbacks::All, + ["GL_EXT_framebuffer_object"], + ) + .write_bindings(gl_generator::GlobalGenerator, &mut file) + .unwrap(); } // TODO: only build the bindings below if we run tests/examples let mut file = File::create(&dest.join("test_gl_bindings.rs")).unwrap(); Registry::new(Api::Gles2, (3, 0), Profile::Core, Fallbacks::All, []) - .write_bindings(gl_generator::StructGenerator, &mut file).unwrap(); + .write_bindings(gl_generator::StructGenerator, &mut file) + .unwrap(); } diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index c40936f355..4e1da451c8 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -2,15 +2,15 @@ extern crate glutin; mod support; -use glutin::GlContext; +use glutin::ContextTrait; use std::io::{self, Write}; fn main() { - let mut events_loop = glutin::EventsLoop::new(); + let mut el = glutin::EventsLoop::new(); // enumerating monitors let monitor = { - for (num, monitor) in events_loop.get_available_monitors().enumerate() { + for (num, monitor) in el.get_available_monitors().enumerate() { println!("Monitor #{}: {:?}", num, monitor.get_name()); } @@ -20,57 +20,67 @@ fn main() { let mut num = String::new(); io::stdin().read_line(&mut num).unwrap(); let num = num.trim().parse().ok().expect("Please enter a number"); - let monitor = events_loop.get_available_monitors().nth(num).expect("Please enter a valid ID"); + let monitor = el + .get_available_monitors() + .nth(num) + .expect("Please enter a valid ID"); println!("Using {:?}", monitor.get_name()); monitor }; - let window = glutin::WindowBuilder::new() + let wb = glutin::WindowBuilder::new() .with_title("Hello world!") .with_fullscreen(Some(monitor)); - let context = glutin::ContextBuilder::new(); - let gl_window = glutin::GlWindow::new(window, context, &events_loop).unwrap(); + let combined_context = glutin::ContextBuilder::new() + .build_combined(wb, &el) + .unwrap(); - let _ = unsafe { gl_window.make_current() }; + unsafe { combined_context.make_current().unwrap() } - let gl = support::load(&gl_window.context()); + let gl = support::load(&combined_context.context()); let mut fullscreen = true; let mut running = true; while running { - events_loop.poll_events(|event| { + el.poll_events(|event| { println!("{:?}", event); match event { glutin::Event::WindowEvent { event, .. } => match event { glutin::WindowEvent::CloseRequested => running = false, glutin::WindowEvent::Resized(logical_size) => { - let dpi_factor = gl_window.get_hidpi_factor(); - gl_window.resize(logical_size.to_physical(dpi_factor)); - }, + let dpi_factor = combined_context.get_hidpi_factor(); + combined_context + .resize(logical_size.to_physical(dpi_factor)); + } glutin::WindowEvent::KeyboardInput { input, .. } => { match input.virtual_keycode { - Some(glutin::VirtualKeyCode::Escape) => running = false, - Some(glutin::VirtualKeyCode::F) if input.state == glutin::ElementState::Pressed => { + Some(glutin::VirtualKeyCode::Escape) => { + running = false + } + Some(glutin::VirtualKeyCode::F) + if input.state + == glutin::ElementState::Pressed => + { let monitor = if fullscreen { None } else { - Some(gl_window.get_current_monitor()) + Some(combined_context.get_current_monitor()) }; - gl_window.set_fullscreen(monitor); + combined_context.set_fullscreen(monitor); fullscreen = !fullscreen; - }, + } _ => (), } - }, + } _ => (), }, - _ => () + _ => (), } }); gl.draw_frame([1.0, 0.5, 0.7, 1.0]); - let _ = gl_window.swap_buffers(); + let _ = combined_context.swap_buffers(); } } diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs index ac47aeeaf6..b1ac0f7f33 100644 --- a/examples/multiwindow.rs +++ b/examples/multiwindow.rs @@ -2,40 +2,48 @@ extern crate glutin; mod support; -use glutin::GlContext; +use glutin::ContextTrait; fn main() { - let mut events_loop = glutin::EventsLoop::new(); + let mut el = glutin::EventsLoop::new(); let mut windows = std::collections::HashMap::new(); for index in 0..3 { let title = format!("Charming Window #{}", index + 1); - let window = glutin::WindowBuilder::new().with_title(title); - let context = glutin::ContextBuilder::new(); - let gl_window = glutin::GlWindow::new(window, context, &events_loop).unwrap(); - let _ = unsafe { gl_window.make_current() }; - let gl = support::load(&gl_window.context()); - let window_id = gl_window.id(); - windows.insert(window_id, (gl_window, gl)); + let wb = glutin::WindowBuilder::new().with_title(title); + let combined_context = glutin::ContextBuilder::new() + .build_combined(wb, &el) + .unwrap(); + unsafe { combined_context.make_current().unwrap() } + let gl = support::load(&combined_context.context()); + let window_id = combined_context.id(); + windows.insert(window_id, (combined_context, gl)); } while !windows.is_empty() { - events_loop.poll_events(|event| { + el.poll_events(|event| { println!("{:?}", event); match event { - glutin::Event::WindowEvent { event, window_id } => match event { - glutin::WindowEvent::Resized(logical_size) => { - let gl_window = &windows[&window_id].0; - let dpi_factor = gl_window.get_hidpi_factor(); - gl_window.resize(logical_size.to_physical(dpi_factor)); - }, - glutin::WindowEvent::CloseRequested => { - if windows.remove(&window_id).is_some() { - println!("Window with ID {:?} has been closed", window_id); + glutin::Event::WindowEvent { event, window_id } => { + match event { + glutin::WindowEvent::Resized(logical_size) => { + let combined_context = &windows[&window_id].0; + let dpi_factor = + combined_context.get_hidpi_factor(); + combined_context + .resize(logical_size.to_physical(dpi_factor)); } - }, - _ => (), - }, + glutin::WindowEvent::CloseRequested => { + if windows.remove(&window_id).is_some() { + println!( + "Window with ID {:?} has been closed", + window_id + ); + } + } + _ => (), + } + } _ => (), } }); diff --git a/examples/separated_context.rs b/examples/separated_context.rs new file mode 100644 index 0000000000..0abbc77b0d --- /dev/null +++ b/examples/separated_context.rs @@ -0,0 +1,57 @@ +extern crate glutin; + +mod support; + +use glutin::ContextTrait; + +fn main() { + let mut el = glutin::EventsLoop::new(); + let win = glutin::WindowBuilder::new() + .with_title("A fantastic window!") + .build(&el) + .unwrap(); + + let separated_context = glutin::ContextBuilder::new() + .build_separated(&win, &el) + .unwrap(); + + unsafe { separated_context.make_current().unwrap() } + + println!( + "Pixel format of the window's GL context: {:?}", + separated_context.get_pixel_format() + ); + + let gl = support::load(&separated_context.context()); + + let mut running = true; + while running { + el.poll_events(|event| { + println!("el {:?}", event); + match event { + glutin::Event::WindowEvent { event, .. } => match event { + glutin::WindowEvent::KeyboardInput { + input: + glutin::KeyboardInput { + virtual_keycode: + Some(glutin::VirtualKeyCode::Escape), + .. + }, + .. + } + | glutin::WindowEvent::CloseRequested => running = false, + glutin::WindowEvent::Resized(logical_size) => { + let dpi_factor = win.get_hidpi_factor(); + separated_context + .resize(logical_size.to_physical(dpi_factor)); + } + _ => (), + }, + _ => (), + } + }); + + gl.draw_frame([1.0, 0.5, 0.7, 1.0]); + let _ = separated_context.swap_buffers(); + } +} diff --git a/examples/sharing.rs b/examples/sharing.rs index fafc1ff3fa..6a86208498 100644 --- a/examples/sharing.rs +++ b/examples/sharing.rs @@ -1,26 +1,33 @@ +//! Requires OpenGL 4.2 minimium. + extern crate glutin; mod support; +use glutin::ContextTrait; use support::gl; -use glutin::GlContext; fn main() { - let mut events_loop = glutin::EventsLoop::new(); + let mut el = glutin::EventsLoop::new(); let mut size = glutin::dpi::PhysicalSize::new(768., 480.); - let context = glutin::ContextBuilder::new(); - let gl_context = glutin::Context::new(&events_loop, context, true).unwrap(); + let headless_context = + glutin::ContextBuilder::new().build_headless(&el).unwrap(); - let window = glutin::WindowBuilder::new().with_title("A fantastic window!") + let wb = glutin::WindowBuilder::new() + .with_title("A fantastic window!") .with_dimensions(glutin::dpi::LogicalSize::from_physical(size, 1.0)); - let context = glutin::ContextBuilder::new() - .with_shared_lists(&gl_context); - let gl_window = glutin::GlWindow::new(window, context, &events_loop).unwrap(); - - let _ = unsafe { gl_window.make_current() }; - println!("Pixel format of the window's GL context: {:?}", gl_window.get_pixel_format()); - let glw = support::load(&gl_window.context()); + let combined_context = glutin::ContextBuilder::new() + .with_shared_lists(&headless_context) + .build_combined(wb, &el) + .unwrap(); + + unsafe { combined_context.make_current().unwrap() } + println!( + "Pixel format of the window's GL context: {:?}", + combined_context.get_pixel_format() + ); + let glw = support::load(&combined_context.context()); let mut render_tex = 0; unsafe { @@ -49,8 +56,8 @@ fn main() { ); } - let _ = unsafe { gl_context.make_current() }; - let glc = support::load(&gl_context); + unsafe { headless_context.make_current().unwrap() } + let glc = support::load(&headless_context); let mut context_fb = 0; unsafe { @@ -68,19 +75,19 @@ fn main() { let mut running = true; while running { - events_loop.poll_events(|event| { + el.poll_events(|event| { println!("{:?}", event); match event { glutin::Event::WindowEvent { event, .. } => match event { glutin::WindowEvent::CloseRequested => running = false, glutin::WindowEvent::Resized(logical_size) => { - let _ = unsafe { gl_window.make_current() }; - let dpi_factor = gl_window.get_hidpi_factor(); + unsafe { combined_context.make_current().unwrap() } + let dpi_factor = combined_context.get_hidpi_factor(); size = logical_size.to_physical(dpi_factor); - gl_window.resize(size); + combined_context.resize(size); unsafe { - let _ = gl_window.make_current(); + let _ = combined_context.make_current(); glw.gl.DeleteTextures(1, &render_tex); glw.gl.DeleteFramebuffers(1, &window_fb); @@ -95,7 +102,10 @@ fn main() { ); glw.gl.GenFramebuffers(1, &mut window_fb); - glw.gl.BindFramebuffer(gl::READ_FRAMEBUFFER, window_fb); + glw.gl.BindFramebuffer( + gl::READ_FRAMEBUFFER, + window_fb, + ); glw.gl.BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0); glw.gl.FramebufferTexture2D( gl::READ_FRAMEBUFFER, @@ -105,7 +115,7 @@ fn main() { 0, ); - let _ = gl_context.make_current(); + let _ = headless_context.make_current(); glc.gl.DeleteFramebuffers(1, &context_fb); glc.gl.GenFramebuffers(1, &mut context_fb); @@ -118,35 +128,46 @@ fn main() { 0, ); - glc.gl.Viewport(0, 0, size.width as _, size.height as _); + glc.gl.Viewport( + 0, + 0, + size.width as _, + size.height as _, + ); } - }, + } _ => (), }, - _ => () + _ => (), } }); - let _ = unsafe { gl_context.make_current() }; + unsafe { headless_context.make_current().unwrap() } glc.draw_frame([1.0, 0.5, 0.7, 1.0]); - let _ = unsafe { gl_window.make_current() }; + unsafe { combined_context.make_current().unwrap() } unsafe { glw.gl.BlitFramebuffer( - 0, 0, size.width as _, size.height as _, - 0, 0, size.width as _, size.height as _, + 0, + 0, + size.width as _, + size.height as _, + 0, + 0, + size.width as _, + size.height as _, gl::COLOR_BUFFER_BIT, gl::NEAREST, ); } - let _ = gl_window.swap_buffers(); + let _ = combined_context.swap_buffers(); } unsafe { - let _ = gl_window.make_current(); + let _ = combined_context.make_current(); glw.gl.DeleteTextures(1, &render_tex); glw.gl.DeleteFramebuffers(1, &window_fb); - let _ = gl_context.make_current(); + let _ = headless_context.make_current(); glc.gl.DeleteFramebuffers(1, &context_fb); } } diff --git a/examples/support/mod.rs b/examples/support/mod.rs index 9fe30c3325..8677a11109 100644 --- a/examples/support/mod.rs +++ b/examples/support/mod.rs @@ -1,7 +1,7 @@ +use glutin::{self, ContextTrait}; use std::ffi::CStr; use std::mem; use std::ptr; -use glutin::{self, GlContext}; pub mod gl { pub use self::Gles2 as Gl; @@ -13,10 +13,13 @@ pub struct Gl { } pub fn load(gl_context: &glutin::Context) -> Gl { - let gl = gl::Gl::load_with(|ptr| gl_context.get_proc_address(ptr) as *const _); + let gl = + gl::Gl::load_with(|ptr| gl_context.get_proc_address(ptr) as *const _); let version = unsafe { - let data = CStr::from_ptr(gl.GetString(gl::VERSION) as *const _).to_bytes().to_vec(); + let data = CStr::from_ptr(gl.GetString(gl::VERSION) as *const _) + .to_bytes() + .to_vec(); String::from_utf8(data).unwrap() }; @@ -24,11 +27,21 @@ pub fn load(gl_context: &glutin::Context) -> Gl { unsafe { let vs = gl.CreateShader(gl::VERTEX_SHADER); - gl.ShaderSource(vs, 1, [VS_SRC.as_ptr() as *const _].as_ptr(), ptr::null()); + gl.ShaderSource( + vs, + 1, + [VS_SRC.as_ptr() as *const _].as_ptr(), + ptr::null(), + ); gl.CompileShader(vs); let fs = gl.CreateShader(gl::FRAGMENT_SHADER); - gl.ShaderSource(fs, 1, [FS_SRC.as_ptr() as *const _].as_ptr(), ptr::null()); + gl.ShaderSource( + fs, + 1, + [FS_SRC.as_ptr() as *const _].as_ptr(), + ptr::null(), + ); gl.CompileShader(fs); let program = gl.CreateProgram(); @@ -40,9 +53,13 @@ pub fn load(gl_context: &glutin::Context) -> Gl { let mut vb = mem::uninitialized(); gl.GenBuffers(1, &mut vb); gl.BindBuffer(gl::ARRAY_BUFFER, vb); - gl.BufferData(gl::ARRAY_BUFFER, - (VERTEX_DATA.len() * mem::size_of::()) as gl::types::GLsizeiptr, - VERTEX_DATA.as_ptr() as *const _, gl::STATIC_DRAW); + gl.BufferData( + gl::ARRAY_BUFFER, + (VERTEX_DATA.len() * mem::size_of::()) + as gl::types::GLsizeiptr, + VERTEX_DATA.as_ptr() as *const _, + gl::STATIC_DRAW, + ); if gl.BindVertexArray.is_loaded() { let mut vao = mem::uninitialized(); @@ -50,14 +67,26 @@ pub fn load(gl_context: &glutin::Context) -> Gl { gl.BindVertexArray(vao); } - let pos_attrib = gl.GetAttribLocation(program, b"position\0".as_ptr() as *const _); - let color_attrib = gl.GetAttribLocation(program, b"color\0".as_ptr() as *const _); - gl.VertexAttribPointer(pos_attrib as gl::types::GLuint, 2, gl::FLOAT, 0, - 5 * mem::size_of::() as gl::types::GLsizei, - ptr::null()); - gl.VertexAttribPointer(color_attrib as gl::types::GLuint, 3, gl::FLOAT, 0, - 5 * mem::size_of::() as gl::types::GLsizei, - (2 * mem::size_of::()) as *const () as *const _); + let pos_attrib = + gl.GetAttribLocation(program, b"position\0".as_ptr() as *const _); + let color_attrib = + gl.GetAttribLocation(program, b"color\0".as_ptr() as *const _); + gl.VertexAttribPointer( + pos_attrib as gl::types::GLuint, + 2, + gl::FLOAT, + 0, + 5 * mem::size_of::() as gl::types::GLsizei, + ptr::null(), + ); + gl.VertexAttribPointer( + color_attrib as gl::types::GLuint, + 3, + gl::FLOAT, + 0, + 5 * mem::size_of::() as gl::types::GLsizei, + (2 * mem::size_of::()) as *const () as *const _, + ); gl.EnableVertexAttribArray(pos_attrib as gl::types::GLuint); gl.EnableVertexAttribArray(color_attrib as gl::types::GLuint); } @@ -75,10 +104,11 @@ impl Gl { } } +#[rustfmt::skip] static VERTEX_DATA: [f32; 15] = [ - -0.5, -0.5, 1.0, 0.0, 0.0, - 0.0, 0.5, 0.0, 1.0, 0.0, - 0.5, -0.5, 0.0, 0.0, 1.0 + -0.5, -0.5, 1.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 1.0, 0.0, + 0.5, -0.5, 0.0, 0.0, 1.0, ]; const VS_SRC: &'static [u8] = b" diff --git a/examples/transparent.rs b/examples/transparent.rs index 58f80f997e..e8dbd697b7 100644 --- a/examples/transparent.rs +++ b/examples/transparent.rs @@ -2,45 +2,55 @@ extern crate glutin; mod support; -use glutin::GlContext; +use glutin::ContextTrait; fn main() { - let mut events_loop = glutin::EventsLoop::new(); - let window = glutin::WindowBuilder::new() + let mut el = glutin::EventsLoop::new(); + let wb = glutin::WindowBuilder::new() .with_title("A transparent window!") .with_decorations(false) .with_transparency(true); - let context = glutin::ContextBuilder::new(); - let gl_window = glutin::GlWindow::new(window, context, &events_loop).unwrap(); + let combined_context = glutin::ContextBuilder::new() + .build_combined(wb, &el) + .unwrap(); - let _ = unsafe { gl_window.make_current() }; + unsafe { combined_context.make_current().unwrap() } - println!("Pixel format of the window's GL context: {:?}", gl_window.get_pixel_format()); + println!( + "Pixel format of the window's GL context: {:?}", + combined_context.get_pixel_format() + ); - let gl = support::load(&gl_window.context()); + let gl = support::load(&combined_context.context()); let mut running = true; while running { - events_loop.poll_events(|event| { + el.poll_events(|event| { println!("{:?}", event); match event { glutin::Event::WindowEvent { event, .. } => match event { glutin::WindowEvent::CloseRequested => running = false, glutin::WindowEvent::Resized(logical_size) => { - let dpi_factor = gl_window.get_hidpi_factor(); - gl_window.resize(logical_size.to_physical(dpi_factor)); - }, - glutin::WindowEvent::KeyboardInput { input: glutin::KeyboardInput { - virtual_keycode: Some(glutin::VirtualKeyCode::Escape), + let dpi_factor = combined_context.get_hidpi_factor(); + combined_context + .resize(logical_size.to_physical(dpi_factor)); + } + glutin::WindowEvent::KeyboardInput { + input: + glutin::KeyboardInput { + virtual_keycode: + Some(glutin::VirtualKeyCode::Escape), + .. + }, .. - }, .. } => running = false, + } => running = false, _ => (), }, - _ => () + _ => (), } }); gl.draw_frame([0.0; 4]); - let _ = gl_window.swap_buffers(); + let _ = combined_context.swap_buffers(); } } diff --git a/examples/window.rs b/examples/window.rs index 0786887516..ead408a951 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -2,38 +2,43 @@ extern crate glutin; mod support; -use glutin::GlContext; +use glutin::ContextTrait; fn main() { - let mut events_loop = glutin::EventsLoop::new(); - let window = glutin::WindowBuilder::new().with_title("A fantastic window!"); - let context = glutin::ContextBuilder::new(); - let gl_window = glutin::GlWindow::new(window, context, &events_loop).unwrap(); + let mut el = glutin::EventsLoop::new(); + let wb = glutin::WindowBuilder::new().with_title("A fantastic window!"); + let combined_context = glutin::ContextBuilder::new() + .build_combined(wb, &el) + .unwrap(); - let _ = unsafe { gl_window.make_current() }; + unsafe { combined_context.make_current().unwrap() } - println!("Pixel format of the window's GL context: {:?}", gl_window.get_pixel_format()); + println!( + "Pixel format of the window's GL context: {:?}", + combined_context.get_pixel_format() + ); - let gl = support::load(&gl_window.context()); + let gl = support::load(&combined_context.context()); let mut running = true; while running { - events_loop.poll_events(|event| { + el.poll_events(|event| { println!("{:?}", event); match event { glutin::Event::WindowEvent { event, .. } => match event { glutin::WindowEvent::CloseRequested => running = false, glutin::WindowEvent::Resized(logical_size) => { - let dpi_factor = gl_window.get_hidpi_factor(); - gl_window.resize(logical_size.to_physical(dpi_factor)); - }, + let dpi_factor = combined_context.get_hidpi_factor(); + combined_context + .resize(logical_size.to_physical(dpi_factor)); + } _ => (), }, - _ => () + _ => (), } }); gl.draw_frame([1.0, 0.5, 0.7, 1.0]); - let _ = gl_window.swap_buffers(); + let _ = combined_context.swap_buffers(); } } diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000000..b05dc191d5 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,3 @@ +wrap_comments = true +max_width = 80 +normalize_comments = true diff --git a/src/api/android/ffi.rs b/src/api/android/ffi.rs index af8c50b344..48ea214394 100644 --- a/src/api/android/ffi.rs +++ b/src/api/android/ffi.rs @@ -9,32 +9,30 @@ use std::os::raw; #[link(name = "android")] #[link(name = "EGL")] #[link(name = "GLESv2")] -extern {} +extern "C" {} -/** - * asset_manager.h - */ +/// asset_manager.h pub type AAssetManager = raw::c_void; -/** - * native_window.h - */ +/// native_window.h pub type ANativeWindow = raw::c_void; -extern { - pub fn ANativeWindow_getHeight(window: *const ANativeWindow) -> libc::int32_t; - pub fn ANativeWindow_getWidth(window: *const ANativeWindow) -> libc::int32_t; +extern "C" { + pub fn ANativeWindow_getHeight( + window: *const ANativeWindow, + ) -> libc::int32_t; + pub fn ANativeWindow_getWidth( + window: *const ANativeWindow, + ) -> libc::int32_t; } -/** - * native_activity.h - */ +/// native_activity.h pub type JavaVM = (); pub type JNIEnv = (); pub type jobject = *const libc::c_void; -pub type AInputQueue = (); // FIXME: wrong -pub type ARect = (); // FIXME: wrong +pub type AInputQueue = (); // FIXME: wrong +pub type ARect = (); // FIXME: wrong #[repr(C)] pub struct ANativeActivity { @@ -52,44 +50,65 @@ pub struct ANativeActivity { #[repr(C)] pub struct ANativeActivityCallbacks { - pub onStart: extern fn(*mut ANativeActivity), - pub onResume: extern fn(*mut ANativeActivity), - pub onSaveInstanceState: extern fn(*mut ANativeActivity, *mut libc::size_t), - pub onPause: extern fn(*mut ANativeActivity), - pub onStop: extern fn(*mut ANativeActivity), - pub onDestroy: extern fn(*mut ANativeActivity), - pub onWindowFocusChanged: extern fn(*mut ANativeActivity, libc::c_int), - pub onNativeWindowCreated: extern fn(*mut ANativeActivity, *const ANativeWindow), - pub onNativeWindowResized: extern fn(*mut ANativeActivity, *const ANativeWindow), - pub onNativeWindowRedrawNeeded: extern fn(*mut ANativeActivity, *const ANativeWindow), - pub onNativeWindowDestroyed: extern fn(*mut ANativeActivity, *const ANativeWindow), - pub onInputQueueCreated: extern fn(*mut ANativeActivity, *mut AInputQueue), - pub onInputQueueDestroyed: extern fn(*mut ANativeActivity, *mut AInputQueue), - pub onContentRectChanged: extern fn(*mut ANativeActivity, *const ARect), - pub onConfigurationChanged: extern fn(*mut ANativeActivity), - pub onLowMemory: extern fn(*mut ANativeActivity), + pub onStart: extern "C" fn(*mut ANativeActivity), + pub onResume: extern "C" fn(*mut ANativeActivity), + pub onSaveInstanceState: + extern "C" fn(*mut ANativeActivity, *mut libc::size_t), + pub onPause: extern "C" fn(*mut ANativeActivity), + pub onStop: extern "C" fn(*mut ANativeActivity), + pub onDestroy: extern "C" fn(*mut ANativeActivity), + pub onWindowFocusChanged: extern "C" fn(*mut ANativeActivity, libc::c_int), + pub onNativeWindowCreated: + extern "C" fn(*mut ANativeActivity, *const ANativeWindow), + pub onNativeWindowResized: + extern "C" fn(*mut ANativeActivity, *const ANativeWindow), + pub onNativeWindowRedrawNeeded: + extern "C" fn(*mut ANativeActivity, *const ANativeWindow), + pub onNativeWindowDestroyed: + extern "C" fn(*mut ANativeActivity, *const ANativeWindow), + pub onInputQueueCreated: + extern "C" fn(*mut ANativeActivity, *mut AInputQueue), + pub onInputQueueDestroyed: + extern "C" fn(*mut ANativeActivity, *mut AInputQueue), + pub onContentRectChanged: extern "C" fn(*mut ANativeActivity, *const ARect), + pub onConfigurationChanged: extern "C" fn(*mut ANativeActivity), + pub onLowMemory: extern "C" fn(*mut ANativeActivity), } -/** - * looper.h - */ +/// looper.h pub type ALooper = (); #[link(name = "android")] -extern { +extern "C" { pub fn ALooper_forThread() -> *const ALooper; pub fn ALooper_acquire(looper: *const ALooper); pub fn ALooper_release(looper: *const ALooper); pub fn ALooper_prepare(opts: libc::c_int) -> *const ALooper; - pub fn ALooper_pollOnce(timeoutMillis: libc::c_int, outFd: *mut libc::c_int, - outEvents: *mut libc::c_int, outData: *mut *mut libc::c_void) -> libc::c_int; - pub fn ALooper_pollAll(timeoutMillis: libc::c_int, outFd: *mut libc::c_int, - outEvents: *mut libc::c_int, outData: *mut *mut libc::c_void) -> libc::c_int; + pub fn ALooper_pollOnce( + timeoutMillis: libc::c_int, + outFd: *mut libc::c_int, + outEvents: *mut libc::c_int, + outData: *mut *mut libc::c_void, + ) -> libc::c_int; + pub fn ALooper_pollAll( + timeoutMillis: libc::c_int, + outFd: *mut libc::c_int, + outEvents: *mut libc::c_int, + outData: *mut *mut libc::c_void, + ) -> libc::c_int; pub fn ALooper_wake(looper: *const ALooper); - pub fn ALooper_addFd(looper: *const ALooper, fd: libc::c_int, ident: libc::c_int, - events: libc::c_int, callback: ALooper_callbackFunc, data: *mut libc::c_void) - -> libc::c_int; - pub fn ALooper_removeFd(looper: *const ALooper, fd: libc::c_int) -> libc::c_int; + pub fn ALooper_addFd( + looper: *const ALooper, + fd: libc::c_int, + ident: libc::c_int, + events: libc::c_int, + callback: ALooper_callbackFunc, + data: *mut libc::c_void, + ) -> libc::c_int; + pub fn ALooper_removeFd( + looper: *const ALooper, + fd: libc::c_int, + ) -> libc::c_int; } pub const ALOOPER_PREPARE_ALLOW_NON_CALLBACKS: libc::c_int = 1 << 0; @@ -105,4 +124,5 @@ pub const ALOOPER_EVENT_ERROR: libc::c_int = 1 << 2; pub const ALOOPER_EVENT_HANGUP: libc::c_int = 1 << 3; pub const ALOOPER_EVENT_INVALID: libc::c_int = 1 << 4; -pub type ALooper_callbackFunc = extern fn(libc::c_int, libc::c_int, *mut libc::c_void) -> libc::c_int; +pub type ALooper_callbackFunc = + extern "C" fn(libc::c_int, libc::c_int, *mut libc::c_void) -> libc::c_int; diff --git a/src/api/android/mod.rs b/src/api/android/mod.rs index 724e3f8660..d9030979bd 100644 --- a/src/api/android/mod.rs +++ b/src/api/android/mod.rs @@ -34,35 +34,39 @@ struct AndroidSyncEventHandler(Arc); impl android_glue::SyncEventHandler for AndroidSyncEventHandler { fn handle(&mut self, event: &android_glue::Event) { match *event { - // 'on_surface_destroyed' Android event can arrive with some delay because multithreading communication. - // Because of that, swap_buffers can be called before processing 'on_surface_destroyed' event, with the - // native window surface already destroyed. EGL generates a BAD_SURFACE error in this situation. - // Set stop to true to prevent swap_buffer call race conditions. + // 'on_surface_destroyed' Android event can arrive with some delay + // because multithreading communication. Because of + // that, swap_buffers can be called before processing + // 'on_surface_destroyed' event, with the native window + // surface already destroyed. EGL generates a BAD_SURFACE error in + // this situation. Set stop to true to prevent + // swap_buffer call race conditions. android_glue::Event::TermWindow => { self.0.stopped.as_ref().unwrap().set(true); - }, - _ => { return; } + } + _ => { + return; + } }; } } impl Context { + #[inline] pub fn new( - window_builder: winit::WindowBuilder, - events_loop: &winit::EventsLoop, + wb: winit::WindowBuilder, + el: &winit::EventsLoop, pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Self>, - ) -> Result<(winit::Window, Self), CreationError> - { - let window = window_builder.build(events_loop)?; + ) -> Result<(winit::Window, Self), CreationError> { + let window = wb.build(el)?; let gl_attr = gl_attr.clone().map_sharing(|c| &c.0.egl_context); let native_window = unsafe { android_glue::get_native_window() }; if native_window.is_null() { return Err(OsError(format!("Android's native window is null"))); } - let egl = egl::ffi::egl::Egl; let native_display = egl::NativeDisplay::Android; - let context = try!(EglContext::new(egl, pf_reqs, &gl_attr, native_display) + let context = try!(EglContext::new(pf_reqs, &gl_attr, native_display) .and_then(|p| p.finish(native_window as *const _))); let ctx = Arc::new(AndroidContext { egl_context: context, @@ -73,7 +77,7 @@ impl Context { android_glue::add_sync_event_handler(handler); let context = Context(ctx.clone()); - events_loop.set_suspend_callback(Some(Box::new(move |suspended| { + el.set_suspend_callback(Some(Box::new(move |suspended| { ctx.stopped.as_ref().unwrap().set(suspended); if suspended { // Android has stopped the activity or sent it to background. @@ -86,7 +90,8 @@ impl Context { // Restore the EGL surface and animation loop. unsafe { let native_window = android_glue::get_native_window(); - ctx.egl_context.on_surface_created(native_window as *const _); + ctx.egl_context + .on_surface_created(native_window as *const _); } } }))); @@ -99,16 +104,11 @@ impl Context { _el: &winit::EventsLoop, pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Context>, - _shareable_with_windowed_contexts: bool, ) -> Result { let gl_attr = gl_attr.clone().map_sharing(|c| &c.0.egl_context); - let context = EglContext::new( - egl::ffi::egl::Egl, - pf_reqs, - &gl_attr, - egl::NativeDisplay::Android - )?; - let context = context.finish_pbuffer((1, 1))?;// TODO: + let context = + EglContext::new(pf_reqs, &gl_attr, egl::NativeDisplay::Android)?; + let context = context.finish_pbuffer((1, 1))?; // TODO: let ctx = Arc::new(AndroidContext { egl_context: context, stopped: None, @@ -116,6 +116,17 @@ impl Context { Ok(Context(ctx)) } + /// See the docs in the crate root file. + #[inline] + pub fn new_separated( + _window: &winit::Window, + _el: &winit::EventsLoop, + _pf_reqs: &PixelFormatRequirements, + _gl_attr: &GlAttributes<&Context>, + ) -> Result { + unimplemented!() + } + #[inline] pub unsafe fn make_current(&self) -> Result<(), ContextError> { if let Some(ref stopped) = self.0.stopped { @@ -128,8 +139,7 @@ impl Context { } #[inline] - pub fn resize(&self, _: u32, _: u32) { - } + pub fn resize(&self, _: u32, _: u32) {} #[inline] pub fn is_current(&self) -> bool { diff --git a/src/api/caca/mod.rs b/src/api/caca/mod.rs index cf7b5bccbd..986af378aa 100644 --- a/src/api/caca/mod.rs +++ b/src/api/caca/mod.rs @@ -1,10 +1,19 @@ -#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] +#![cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] #![allow(unused_variables, dead_code)] +use api::osmesa::OsMesaContext; use libc; -use api::osmesa::{OsMesaContext}; -use {Api, ContextError, CreationError, GlAttributes, PixelFormat, PixelFormatRequirements}; +use { + Api, ContextError, CreationError, GlAttributes, PixelFormat, + PixelFormatRequirements, +}; use std::path::Path; use std::ptr; @@ -19,45 +28,61 @@ pub struct Context { } impl Context { - pub fn new( window_outer_size: (u32, u32), pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Context>, - ) -> Result - { + ) -> Result { let gl_attr = gl_attr.clone().map_sharing(|w| &w.opengl); let opengl = OsMesaContext::new(window_outer_size, pf_reqs, &gl_attr)?; let opengl_dimensions = opengl.get_dimensions(); let libcaca = match ffi::LibCaca::open(&Path::new("libcaca.so.0")) { - Err(_) => return Err(CreationError::NotSupported("could not find libcaca.so")), - Ok(l) => l + Err(_) => { + return Err(CreationError::NotSupported( + "could not find libcaca.so", + )); + } + Ok(l) => l, }; let display = unsafe { (libcaca.caca_create_display)(ptr::null_mut()) }; if display.is_null() { - return Err(CreationError::OsError("caca_create_display failed".to_string())); + return Err(CreationError::OsError( + "caca_create_display failed".to_string(), + )); } let dither = unsafe { #[cfg(target_endian = "little")] - fn get_masks() -> (u32, u32, u32, u32) { (0xff, 0xff00, 0xff0000, 0xff000000) } + fn get_masks() -> (u32, u32, u32, u32) { + (0xff, 0xff00, 0xff0000, 0xff000000) + } #[cfg(target_endian = "big")] - fn get_masks() -> (u32, u32, u32, u32) { (0xff000000, 0xff0000, 0xff00, 0xff) } + fn get_masks() -> (u32, u32, u32, u32) { + (0xff000000, 0xff0000, 0xff00, 0xff) + } let masks = get_masks(); - (libcaca.caca_create_dither)(32, opengl_dimensions.0 as libc::c_int, - opengl_dimensions.1 as libc::c_int, - opengl_dimensions.0 as libc::c_int * 4, - masks.0, masks.1, masks.2, masks.3) + (libcaca.caca_create_dither)( + 32, + opengl_dimensions.0 as libc::c_int, + opengl_dimensions.1 as libc::c_int, + opengl_dimensions.0 as libc::c_int * 4, + masks.0, + masks.1, + masks.2, + masks.3, + ) }; if dither.is_null() { unsafe { (libcaca.caca_free_display)(display) }; - return Err(CreationError::OsError("caca_create_dither failed".to_string())); + return Err(CreationError::OsError( + "caca_create_dither failed".to_string(), + )); } Ok(Context { @@ -90,12 +115,23 @@ impl Context { let width = (self.libcaca.caca_get_canvas_width)(canvas); let height = (self.libcaca.caca_get_canvas_height)(canvas); - let buffer = self.opengl.get_framebuffer().chunks(self.opengl.get_dimensions().0 as usize) - .flat_map(|i| i.iter().cloned()).rev().collect::>(); - - (self.libcaca.caca_dither_bitmap)(canvas, 0, 0, width as libc::c_int, - height as libc::c_int, self.dither, - buffer.as_ptr() as *const _); + let buffer = self + .opengl + .get_framebuffer() + .chunks(self.opengl.get_dimensions().0 as usize) + .flat_map(|i| i.iter().cloned()) + .rev() + .collect::>(); + + (self.libcaca.caca_dither_bitmap)( + canvas, + 0, + 0, + width as libc::c_int, + height as libc::c_int, + self.dither, + buffer.as_ptr() as *const _, + ); (self.libcaca.caca_refresh_display)(self.display); }; diff --git a/src/api/dlloader/mod.rs b/src/api/dlloader/mod.rs new file mode 100644 index 0000000000..ef72b47b50 --- /dev/null +++ b/src/api/dlloader/mod.rs @@ -0,0 +1,65 @@ +#![cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] +use std::ffi::CString; +use std::ops::{Deref, DerefMut}; +use std::sync::Arc; + +use libloading::Library; + +#[derive(Clone)] +pub struct SymWrapper { + inner: T, + _lib: Arc, +} + +pub trait SymTrait { + fn load_with(loadfn: F) -> Self + where + F: FnMut(&'static str) -> *const std::os::raw::c_void; +} + +impl SymWrapper { + pub fn new(lib_paths: Vec<&str>) -> Result { + for path in lib_paths { + let lib = Library::new(path); + if lib.is_ok() { + return Ok(SymWrapper { + inner: T::load_with(|sym| unsafe { + lib.as_ref() + .unwrap() + .get( + CString::new(sym.as_bytes()) + .unwrap() + .as_bytes_with_nul(), + ) + .map(|sym| *sym) + .unwrap_or(std::ptr::null_mut()) + }), + _lib: Arc::new(lib.unwrap()), + }); + } + } + + Err(()) + } +} + +impl Deref for SymWrapper { + type Target = T; + + fn deref(&self) -> &T { + &self.inner + } +} + +impl DerefMut for SymWrapper { + fn deref_mut(&mut self) -> &mut T { + &mut self.inner + } +} diff --git a/src/api/dlopen.rs b/src/api/dlopen.rs deleted file mode 100644 index 84796d1f1e..0000000000 --- a/src/api/dlopen.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] -#![allow(dead_code)] - -use std::os::raw::{c_void, c_char, c_int}; - -pub const RTLD_LAZY: c_int = 0x001; -pub const RTLD_NOW: c_int = 0x002; - -#[link(name = "dl")] -extern { - pub fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void; - pub fn dlerror() -> *mut c_char; - pub fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void; - pub fn dlclose(handle: *mut c_void) -> c_int; -} diff --git a/src/api/egl/ffi.rs b/src/api/egl/ffi.rs index 91d992b54f..a765f858df 100644 --- a/src/api/egl/ffi.rs +++ b/src/api/egl/ffi.rs @@ -28,7 +28,7 @@ pub type khronos_uint64_t = libc::uint64_t; pub type khronos_ssize_t = libc::c_long; pub type EGLint = libc::int32_t; pub type EGLNativeDisplayType = *const libc::c_void; -pub type EGLNativePixmapType = *const libc::c_void; // FIXME: egl_native_pixmap_t instead +pub type EGLNativePixmapType = *const libc::c_void; // FIXME: egl_native_pixmap_t instead #[cfg(target_os = "windows")] pub type EGLNativeWindowType = winapi::shared::windef::HWND; @@ -36,5 +36,10 @@ pub type EGLNativeWindowType = winapi::shared::windef::HWND; pub type EGLNativeWindowType = *const libc::c_void; #[cfg(target_os = "android")] pub type EGLNativeWindowType = *const libc::c_void; -#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] pub type EGLNativeWindowType = *const libc::c_void; diff --git a/src/api/egl/mod.rs b/src/api/egl/mod.rs index a1be4b782b..92ff5549fc 100644 --- a/src/api/egl/mod.rs +++ b/src/api/egl/mod.rs @@ -1,8 +1,15 @@ -#![cfg(any(target_os = "windows", target_os = "linux", target_os = "android", - target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", - target_os = "openbsd"))] +#![cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] #![allow(unused_variables)] +use Api; use ContextError; use CreationError; use GlAttributes; @@ -11,15 +18,81 @@ use PixelFormat; use PixelFormatRequirements; use ReleaseBehavior; use Robustness; -use Api; +use std::cell::Cell; use std::ffi::{CStr, CString}; -use std::os::raw::{c_void, c_int}; +use std::ops::{Deref, DerefMut}; +use std::os::raw::{c_int, c_void}; use std::{mem, ptr}; -use std::cell::Cell; pub mod ffi; +#[cfg(not(target_os = "android"))] +mod egl { + use super::ffi; + use api::dlloader::{SymTrait, SymWrapper}; + + #[derive(Clone)] + pub struct Egl(pub SymWrapper); + + /// Because `*const libc::c_void` doesn't implement `Sync`. + unsafe impl Sync for Egl {} + + impl SymTrait for ffi::egl::Egl { + fn load_with(loadfn: F) -> Self + where + F: FnMut(&'static str) -> *const std::os::raw::c_void, + { + Self::load_with(loadfn) + } + } + + impl Egl { + pub fn new() -> Result { + #[cfg(target_os = "windows")] + let paths = vec!["libEGL.dll", "atioglxx.dll"]; + + #[cfg(not(target_os = "windows"))] + let paths = vec!["libEGL.so.1", "libEGL.so"]; + + SymWrapper::new(paths).map(|i| Egl(i)) + } + } +} + +#[cfg(target_os = "android")] +mod egl { + use super::ffi; + + #[derive(Clone)] + pub struct Egl(pub ffi::egl::Egl); + + impl Egl { + pub fn new() -> Result { + Ok(Egl(ffi::egl::Egl)) + } + } +} +pub use self::egl::Egl; + +impl Deref for Egl { + type Target = ffi::egl::Egl; + + fn deref(&self) -> &ffi::egl::Egl { + &self.0 + } +} + +impl DerefMut for Egl { + fn deref_mut(&mut self) -> &mut ffi::egl::Egl { + &mut self.0 + } +} + +lazy_static! { + pub static ref EGL: Option = Egl::new().ok(); +} + /// Specifies the type of display passed as `native_display`. #[allow(dead_code)] pub enum NativeDisplay { @@ -33,12 +106,12 @@ pub enum NativeDisplay { Android, // TODO: should be `EGLDeviceEXT` Device(ffi::EGLNativeDisplayType), - /// Don't specify any display type. Useful on windows. `None` means `EGL_DEFAULT_DISPLAY`. + /// Don't specify any display type. Useful on windows. `None` means + /// `EGL_DEFAULT_DISPLAY`. Other(Option), } pub struct Context { - egl: ffi::egl::Egl, display: ffi::egl::types::EGLDisplay, context: ffi::egl::types::EGLContext, surface: Cell, @@ -50,111 +123,171 @@ pub struct Context { #[cfg(target_os = "android")] #[inline] -fn get_native_display(egl: &ffi::egl::Egl, - native_display: NativeDisplay) -> *const c_void { +fn get_native_display( + egl: &Egl, + native_display: NativeDisplay, +) -> *const c_void { unsafe { egl.GetDisplay(ffi::egl::DEFAULT_DISPLAY as *mut _) } } #[cfg(not(target_os = "android"))] -fn get_native_display(egl: &ffi::egl::Egl, - native_display: NativeDisplay) -> *const c_void { - // the first step is to query the list of extensions without any display, if supported +fn get_native_display( + egl: &Egl, + native_display: NativeDisplay, +) -> *const c_void { + // the first step is to query the list of extensions without any display, if + // supported let dp_extensions = unsafe { - let p = egl.QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32); + let p = + egl.QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32); - // this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise - // `eglQueryString` returns an error + // this possibility is available only with EGL 1.5 or + // EGL_EXT_platform_base, otherwise `eglQueryString` returns an + // error if p.is_null() { vec![] } else { let p = CStr::from_ptr(p); - let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| format!("")); + let list = String::from_utf8(p.to_bytes().to_vec()) + .unwrap_or_else(|_| format!("")); list.split(' ').map(|e| e.to_string()).collect::>() } }; - let has_dp_extension = |e: &str| dp_extensions.iter().find(|s| s == &e).is_some(); + let has_dp_extension = + |e: &str| dp_extensions.iter().find(|s| s == &e).is_some(); match native_display { - // Note: Some EGL implementations are missing the `eglGetPlatformDisplay(EXT)` symbol - // despite reporting `EGL_EXT_platform_base`. I'm pretty sure this is a bug. - // Therefore we detect whether the symbol is loaded in addition to checking for - // extensions. - NativeDisplay::X11(display) if has_dp_extension("EGL_KHR_platform_x11") && - egl.GetPlatformDisplay.is_loaded() => + // Note: Some EGL implementations are missing the + // `eglGetPlatformDisplay(EXT)` symbol despite reporting + // `EGL_EXT_platform_base`. I'm pretty sure this is a bug. + // Therefore we detect whether the symbol is loaded in addition to + // checking for extensions. + NativeDisplay::X11(display) + if has_dp_extension("EGL_KHR_platform_x11") + && egl.GetPlatformDisplay.is_loaded() => { let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); // TODO: `PLATFORM_X11_SCREEN_KHR` - unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, d as *mut _, - ptr::null()) } - }, + unsafe { + egl.GetPlatformDisplay( + ffi::egl::PLATFORM_X11_KHR, + d as *mut _, + ptr::null(), + ) + } + } - NativeDisplay::X11(display) if has_dp_extension("EGL_EXT_platform_x11") && - egl.GetPlatformDisplayEXT.is_loaded() => + NativeDisplay::X11(display) + if has_dp_extension("EGL_EXT_platform_x11") + && egl.GetPlatformDisplayEXT.is_loaded() => { let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); // TODO: `PLATFORM_X11_SCREEN_EXT` - unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, d as *mut _, - ptr::null()) } - }, + unsafe { + egl.GetPlatformDisplayEXT( + ffi::egl::PLATFORM_X11_EXT, + d as *mut _, + ptr::null(), + ) + } + } - NativeDisplay::Gbm(display) if has_dp_extension("EGL_KHR_platform_gbm") && - egl.GetPlatformDisplay.is_loaded() => + NativeDisplay::Gbm(display) + if has_dp_extension("EGL_KHR_platform_gbm") + && egl.GetPlatformDisplay.is_loaded() => { let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, d as *mut _, - ptr::null()) } - }, + unsafe { + egl.GetPlatformDisplay( + ffi::egl::PLATFORM_GBM_KHR, + d as *mut _, + ptr::null(), + ) + } + } - NativeDisplay::Gbm(display) if has_dp_extension("EGL_MESA_platform_gbm") && - egl.GetPlatformDisplayEXT.is_loaded() => + NativeDisplay::Gbm(display) + if has_dp_extension("EGL_MESA_platform_gbm") + && egl.GetPlatformDisplayEXT.is_loaded() => { let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_KHR, d as *mut _, - ptr::null()) } - }, + unsafe { + egl.GetPlatformDisplayEXT( + ffi::egl::PLATFORM_GBM_KHR, + d as *mut _, + ptr::null(), + ) + } + } - NativeDisplay::Wayland(display) if has_dp_extension("EGL_KHR_platform_wayland") && - egl.GetPlatformDisplay.is_loaded() => + NativeDisplay::Wayland(display) + if has_dp_extension("EGL_KHR_platform_wayland") + && egl.GetPlatformDisplay.is_loaded() => { let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_WAYLAND_KHR, d as *mut _, - ptr::null()) } - }, + unsafe { + egl.GetPlatformDisplay( + ffi::egl::PLATFORM_WAYLAND_KHR, + d as *mut _, + ptr::null(), + ) + } + } - NativeDisplay::Wayland(display) if has_dp_extension("EGL_EXT_platform_wayland") && - egl.GetPlatformDisplayEXT.is_loaded() => + NativeDisplay::Wayland(display) + if has_dp_extension("EGL_EXT_platform_wayland") + && egl.GetPlatformDisplayEXT.is_loaded() => { let d = display.unwrap_or(ffi::egl::DEFAULT_DISPLAY as *const _); - unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_WAYLAND_EXT, d as *mut _, - ptr::null()) } - }, - - // TODO: This will never be reached right now, as the android egl bindings - // use the static generator, so can't rely on GetPlatformDisplay(EXT). - NativeDisplay::Android if has_dp_extension("EGL_KHR_platform_android") && - egl.GetPlatformDisplay.is_loaded() => - { - unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_ANDROID_KHR, - ffi::egl::DEFAULT_DISPLAY as *mut _, ptr::null()) } - }, + unsafe { + egl.GetPlatformDisplayEXT( + ffi::egl::PLATFORM_WAYLAND_EXT, + d as *mut _, + ptr::null(), + ) + } + } - NativeDisplay::Device(display) if has_dp_extension("EGL_EXT_platform_device") && - egl.GetPlatformDisplay.is_loaded() => - { - unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_DEVICE_EXT, display as *mut _, - ptr::null()) } - }, + // TODO: This will never be reached right now, as the android egl + // bindings use the static generator, so can't rely on + // GetPlatformDisplay(EXT). + NativeDisplay::Android + if has_dp_extension("EGL_KHR_platform_android") + && egl.GetPlatformDisplay.is_loaded() => + unsafe { + egl.GetPlatformDisplay( + ffi::egl::PLATFORM_ANDROID_KHR, + ffi::egl::DEFAULT_DISPLAY as *mut _, + ptr::null(), + ) + } - NativeDisplay::X11(Some(display)) | NativeDisplay::Gbm(Some(display)) | - NativeDisplay::Wayland(Some(display)) | NativeDisplay::Device(display) | - NativeDisplay::Other(Some(display)) => { - unsafe { egl.GetDisplay(display as *mut _) } + NativeDisplay::Device(display) + if has_dp_extension("EGL_EXT_platform_device") + && egl.GetPlatformDisplay.is_loaded() => + unsafe { + egl.GetPlatformDisplay( + ffi::egl::PLATFORM_DEVICE_EXT, + display as *mut _, + ptr::null(), + ) } - NativeDisplay::X11(None) | NativeDisplay::Gbm(None) | NativeDisplay::Wayland(None) | - NativeDisplay::Android | NativeDisplay::Other(None) => { - unsafe { egl.GetDisplay(ffi::egl::DEFAULT_DISPLAY as *mut _) } + NativeDisplay::X11(Some(display)) + | NativeDisplay::Gbm(Some(display)) + | NativeDisplay::Wayland(Some(display)) + | NativeDisplay::Device(display) + | NativeDisplay::Other(Some(display)) => unsafe { + egl.GetDisplay(display as *mut _) + }, + + NativeDisplay::X11(None) + | NativeDisplay::Gbm(None) + | NativeDisplay::Wayland(None) + | NativeDisplay::Android + | NativeDisplay::Other(None) => unsafe { + egl.GetDisplay(ffi::egl::DEFAULT_DISPLAY as *mut _) }, } } @@ -164,23 +297,21 @@ impl Context { /// /// This function initializes some things and chooses the pixel format. /// - /// To finish the process, you must call `.finish(window)` on the `ContextPrototype`. + /// To finish the process, you must call `.finish(window)` on the + /// `ContextPrototype`. pub fn new<'a>( - egl: ffi::egl::Egl, pf_reqs: &PixelFormatRequirements, opengl: &'a GlAttributes<&'a Context>, native_display: NativeDisplay, - ) -> Result, CreationError> - { - if opengl.sharing.is_some() { - unimplemented!() - } - + ) -> Result, CreationError> { + let egl = EGL.as_ref().unwrap(); // calling `eglGetDisplay` or equivalent - let display = get_native_display(&egl, native_display); + let display = get_native_display(egl, native_display); if display.is_null() { - return Err(CreationError::OsError("Could not create EGL display object".to_string())); + return Err(CreationError::OsError( + "Could not create EGL display object".to_string(), + )); } let egl_version = unsafe { @@ -188,19 +319,25 @@ impl Context { let mut minor: ffi::egl::types::EGLint = mem::uninitialized(); if egl.Initialize(display, &mut major, &mut minor) == 0 { - return Err(CreationError::OsError(format!("eglInitialize failed"))) + return Err(CreationError::OsError(format!( + "eglInitialize failed" + ))); } (major, minor) }; - // the list of extensions supported by the client once initialized is different from the - // list of extensions obtained earlier + // the list of extensions supported by the client once initialized is + // different from the list of extensions obtained earlier let extensions = if egl_version >= (1, 2) { - let p = unsafe { CStr::from_ptr(egl.QueryString(display, ffi::egl::EXTENSIONS as i32)) }; - let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| format!("")); + let p = unsafe { + CStr::from_ptr( + egl.QueryString(display, ffi::egl::EXTENSIONS as i32), + ) + }; + let list = String::from_utf8(p.to_bytes().to_vec()) + .unwrap_or_else(|_| format!("")); list.split(' ').map(|e| e.to_string()).collect::>() - } else { vec![] }; @@ -215,20 +352,24 @@ impl Context { } else if egl.BindAPI(ffi::egl::OPENGL_ES_API) != 0 { (None, Api::OpenGlEs) } else { - return Err(CreationError::OpenGlVersionNotSupported); + return Err( + CreationError::OpenGlVersionNotSupported, + ); } } else { (None, Api::OpenGlEs) } - }, + } GlRequest::Specific(Api::OpenGlEs, version) => { if egl_version >= (1, 2) { if egl.BindAPI(ffi::egl::OPENGL_ES_API) == 0 { - return Err(CreationError::OpenGlVersionNotSupported); + return Err( + CreationError::OpenGlVersionNotSupported, + ); } } (Some(version), Api::OpenGlEs) - }, + } GlRequest::Specific(Api::OpenGl, version) => { if egl_version < (1, 4) { return Err(CreationError::OpenGlVersionNotSupported); @@ -237,31 +378,37 @@ impl Context { return Err(CreationError::OpenGlVersionNotSupported); } (Some(version), Api::OpenGl) - }, - GlRequest::Specific(_, _) => return Err(CreationError::OpenGlVersionNotSupported), - GlRequest::GlThenGles { opengles_version, opengl_version } => { + } + GlRequest::Specific(_, _) => { + return Err(CreationError::OpenGlVersionNotSupported); + } + GlRequest::GlThenGles { + opengles_version, + opengl_version, + } => { if egl_version >= (1, 4) { if egl.BindAPI(ffi::egl::OPENGL_API) != 0 { (Some(opengl_version), Api::OpenGl) } else if egl.BindAPI(ffi::egl::OPENGL_ES_API) != 0 { (Some(opengles_version), Api::OpenGlEs) } else { - return Err(CreationError::OpenGlVersionNotSupported); + return Err( + CreationError::OpenGlVersionNotSupported, + ); } } else { (Some(opengles_version), Api::OpenGlEs) } - }, + } } }; let (config_id, pixel_format) = unsafe { - choose_fbconfig(&egl, display, &egl_version, api, version, pf_reqs)? + choose_fbconfig(egl, display, &egl_version, api, version, pf_reqs)? }; Ok(ContextPrototype { opengl: opengl, - egl: egl, display: display, egl_version: egl_version, extensions: extensions, @@ -273,14 +420,22 @@ impl Context { } pub unsafe fn make_current(&self) -> Result<(), ContextError> { - let ret = self.egl.MakeCurrent(self.display, self.surface.get(), self.surface.get(), self.context); + let egl = EGL.as_ref().unwrap(); + let ret = egl.MakeCurrent( + self.display, + self.surface.get(), + self.surface.get(), + self.context, + ); if ret == 0 { - match self.egl.GetError() as u32 { + match egl.GetError() as u32 { ffi::egl::CONTEXT_LOST => return Err(ContextError::ContextLost), - err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err) + err => panic!( + "eglMakeCurrent failed (eglGetError returned 0x{:x})", + err + ), } - } else { Ok(()) } @@ -288,33 +443,34 @@ impl Context { #[inline] pub fn is_current(&self) -> bool { - unsafe { self.egl.GetCurrentContext() == self.context } + let egl = EGL.as_ref().unwrap(); + unsafe { egl.GetCurrentContext() == self.context } } pub fn get_proc_address(&self, addr: &str) -> *const () { + let egl = EGL.as_ref().unwrap(); let addr = CString::new(addr.as_bytes()).unwrap(); let addr = addr.as_ptr(); - unsafe { - self.egl.GetProcAddress(addr) as *const _ - } + unsafe { egl.GetProcAddress(addr) as *const _ } } #[inline] pub fn swap_buffers(&self) -> Result<(), ContextError> { + let egl = EGL.as_ref().unwrap(); if self.surface.get() == ffi::egl::NO_SURFACE { return Err(ContextError::ContextLost); } - let ret = unsafe { - self.egl.SwapBuffers(self.display, self.surface.get()) - }; + let ret = unsafe { egl.SwapBuffers(self.display, self.surface.get()) }; if ret == 0 { - match unsafe { self.egl.GetError() } as u32 { + match unsafe { egl.GetError() } as u32 { ffi::egl::CONTEXT_LOST => return Err(ContextError::ContextLost), - err => panic!("eglSwapBuffers failed (eglGetError returned 0x{:x})", err) + err => panic!( + "eglSwapBuffers failed (eglGetError returned 0x{:x})", + err + ), } - } else { Ok(()) } @@ -345,15 +501,29 @@ impl Context { // Create a new surface and attach it to the recreated ANativeWindow. // Restore the EGLContext. #[cfg(target_os = "android")] - pub unsafe fn on_surface_created(&self, native_window: ffi::EGLNativeWindowType) { + pub unsafe fn on_surface_created( + &self, + native_window: ffi::EGLNativeWindowType, + ) { + let egl = EGL.as_ref().unwrap(); if self.surface.get() != ffi::egl::NO_SURFACE { return; } - self.surface.set(self.egl.CreateWindowSurface(self.display, self.config_id, native_window, ptr::null())); + self.surface.set(egl.CreateWindowSurface( + self.display, + self.config_id, + native_window, + ptr::null(), + )); if self.surface.get().is_null() { panic!("on_surface_created: eglCreateWindowSurface failed") } - let ret = self.egl.MakeCurrent(self.display, self.surface.get(), self.surface.get(), self.context); + let ret = egl.MakeCurrent( + self.display, + self.surface.get(), + self.surface.get(), + self.context, + ); if ret == 0 { panic!("on_surface_created: eglMakeCurrent failed"); } @@ -365,15 +535,21 @@ impl Context { // The EGLContext is not destroyed so it can be restored later. #[cfg(target_os = "android")] pub unsafe fn on_surface_destroyed(&self) { + let egl = EGL.as_ref().unwrap(); if self.surface.get() == ffi::egl::NO_SURFACE { return; } - let ret = self.egl.MakeCurrent(self.display, ffi::egl::NO_SURFACE, ffi::egl::NO_SURFACE, ffi::egl::NO_CONTEXT); + let ret = egl.MakeCurrent( + self.display, + ffi::egl::NO_SURFACE, + ffi::egl::NO_SURFACE, + ffi::egl::NO_CONTEXT, + ); if ret == 0 { panic!("on_surface_destroyed: eglMakeCurrent failed"); } - self.egl.DestroySurface(self.display, self.surface.get()); + egl.DestroySurface(self.display, self.surface.get()); self.surface.set(ffi::egl::NO_SURFACE); } } @@ -383,19 +559,19 @@ unsafe impl Sync for Context {} impl Drop for Context { fn drop(&mut self) { + let egl = EGL.as_ref().unwrap(); unsafe { - // we don't call MakeCurrent(0, 0) because we are not sure that the context - // is still the current one - self.egl.DestroyContext(self.display, self.context); - self.egl.DestroySurface(self.display, self.surface.get()); - self.egl.Terminate(self.display); + // we don't call MakeCurrent(0, 0) because we are not sure that the + // context is still the current one + egl.DestroyContext(self.display, self.context); + egl.DestroySurface(self.display, self.surface.get()); + egl.Terminate(self.display); } } } pub struct ContextPrototype<'a> { opengl: &'a GlAttributes<&'a Context>, - egl: ffi::egl::Egl, display: ffi::egl::types::EGLDisplay, egl_version: (ffi::egl::types::EGLint, ffi::egl::types::EGLint), extensions: Vec, @@ -407,22 +583,38 @@ pub struct ContextPrototype<'a> { impl<'a> ContextPrototype<'a> { pub fn get_native_visual_id(&self) -> ffi::egl::types::EGLint { + let egl = EGL.as_ref().unwrap(); let mut value = unsafe { mem::uninitialized() }; - let ret = unsafe { self.egl.GetConfigAttrib(self.display, self.config_id, - ffi::egl::NATIVE_VISUAL_ID - as ffi::egl::types::EGLint, &mut value) }; - if ret == 0 { panic!("eglGetConfigAttrib failed") }; + let ret = unsafe { + egl.GetConfigAttrib( + self.display, + self.config_id, + ffi::egl::NATIVE_VISUAL_ID as ffi::egl::types::EGLint, + &mut value, + ) + }; + if ret == 0 { + panic!("eglGetConfigAttrib failed") + }; value } - pub fn finish(self, native_window: ffi::EGLNativeWindowType) - -> Result - { + pub fn finish( + self, + native_window: ffi::EGLNativeWindowType, + ) -> Result { + let egl = EGL.as_ref().unwrap(); let surface = unsafe { - let surface = self.egl.CreateWindowSurface(self.display, self.config_id, native_window, - ptr::null()); + let surface = egl.CreateWindowSurface( + self.display, + self.config_id, + native_window, + ptr::null(), + ); if surface.is_null() { - return Err(CreationError::OsError(format!("eglCreateWindowSurface failed"))) + return Err(CreationError::OsError(format!( + "eglCreateWindowSurface failed" + ))); } surface }; @@ -431,18 +623,29 @@ impl<'a> ContextPrototype<'a> { } #[cfg(any(target_os = "android", target_os = "windows"))] - pub fn finish_pbuffer(self, dimensions: (u32, u32)) -> Result { + pub fn finish_pbuffer( + self, + dimensions: (u32, u32), + ) -> Result { + let egl = EGL.as_ref().unwrap(); let attrs = &[ - ffi::egl::WIDTH as c_int, dimensions.0 as c_int, - ffi::egl::HEIGHT as c_int, dimensions.1 as c_int, + ffi::egl::WIDTH as c_int, + dimensions.0 as c_int, + ffi::egl::HEIGHT as c_int, + dimensions.1 as c_int, ffi::egl::NONE as c_int, ]; let surface = unsafe { - let surface = self.egl.CreatePbufferSurface(self.display, self.config_id, - attrs.as_ptr()); + let surface = egl.CreatePbufferSurface( + self.display, + self.config_id, + attrs.as_ptr(), + ); if surface.is_null() { - return Err(CreationError::OsError(format!("eglCreatePbufferSurface failed"))) + return Err(CreationError::OsError(format!( + "eglCreatePbufferSurface failed" + ))); } surface }; @@ -450,49 +653,93 @@ impl<'a> ContextPrototype<'a> { self.finish_impl(surface) } - fn finish_impl(self, surface: ffi::egl::types::EGLSurface) - -> Result - { + fn finish_impl( + self, + surface: ffi::egl::types::EGLSurface, + ) -> Result { + let share = match self.opengl.sharing { + Some(ctx) => ctx.context, + None => ptr::null(), + }; + let context = unsafe { if let Some(version) = self.version { - create_context(&self.egl, self.display, &self.egl_version, - &self.extensions, self.api, version, self.config_id, - self.opengl.debug, self.opengl.robustness)? - + create_context( + self.display, + &self.egl_version, + &self.extensions, + self.api, + version, + self.config_id, + self.opengl.debug, + self.opengl.robustness, + share, + )? } else if self.api == Api::OpenGlEs { - if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version, - &self.extensions, self.api, (2, 0), self.config_id, - self.opengl.debug, self.opengl.robustness) - { - ctxt - } else if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version, - &self.extensions, self.api, (1, 0), - self.config_id, self.opengl.debug, - self.opengl.robustness) - { - ctxt + if let Ok(ctx) = create_context( + self.display, + &self.egl_version, + &self.extensions, + self.api, + (2, 0), + self.config_id, + self.opengl.debug, + self.opengl.robustness, + share, + ) { + ctx + } else if let Ok(ctx) = create_context( + self.display, + &self.egl_version, + &self.extensions, + self.api, + (1, 0), + self.config_id, + self.opengl.debug, + self.opengl.robustness, + share, + ) { + ctx } else { return Err(CreationError::OpenGlVersionNotSupported); } - } else { - if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version, - &self.extensions, self.api, (3, 2), self.config_id, - self.opengl.debug, self.opengl.robustness) - { - ctxt - } else if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version, - &self.extensions, self.api, (3, 1), - self.config_id, self.opengl.debug, - self.opengl.robustness) - { - ctxt - } else if let Ok(ctxt) = create_context(&self.egl, self.display, &self.egl_version, - &self.extensions, self.api, (1, 0), - self.config_id, self.opengl.debug, - self.opengl.robustness) - { - ctxt + if let Ok(ctx) = create_context( + self.display, + &self.egl_version, + &self.extensions, + self.api, + (3, 2), + self.config_id, + self.opengl.debug, + self.opengl.robustness, + share, + ) { + ctx + } else if let Ok(ctx) = create_context( + self.display, + &self.egl_version, + &self.extensions, + self.api, + (3, 1), + self.config_id, + self.opengl.debug, + self.opengl.robustness, + share, + ) { + ctx + } else if let Ok(ctx) = create_context( + self.display, + &self.egl_version, + &self.extensions, + self.api, + (1, 0), + self.config_id, + self.opengl.debug, + self.opengl.robustness, + share, + ) { + ctx } else { return Err(CreationError::OpenGlVersionNotSupported); } @@ -500,7 +747,6 @@ impl<'a> ContextPrototype<'a> { }; Ok(Context { - egl: self.egl, display: self.display, context: context, surface: Cell::new(surface), @@ -512,11 +758,14 @@ impl<'a> ContextPrototype<'a> { } } -unsafe fn choose_fbconfig(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisplay, - egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), - api: Api, version: Option<(u8, u8)>, reqs: &PixelFormatRequirements) - -> Result<(ffi::egl::types::EGLConfig, PixelFormat), CreationError> -{ +unsafe fn choose_fbconfig( + egl: &Egl, + display: ffi::egl::types::EGLDisplay, + egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), + api: Api, + version: Option<(u8, u8)>, + reqs: &PixelFormatRequirements, +) -> Result<(ffi::egl::types::EGLConfig, PixelFormat), CreationError> { let descriptor = { let mut out: Vec = Vec::with_capacity(37); @@ -532,19 +781,23 @@ unsafe fn choose_fbconfig(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisp match (api, version) { (Api::OpenGlEs, Some((3, _))) => { - if egl_version < &(1, 3) { return Err(CreationError::NoAvailablePixelFormat); } + if egl_version < &(1, 3) { + return Err(CreationError::NoAvailablePixelFormat); + } out.push(ffi::egl::RENDERABLE_TYPE as c_int); out.push(ffi::egl::OPENGL_ES3_BIT as c_int); out.push(ffi::egl::CONFORMANT as c_int); out.push(ffi::egl::OPENGL_ES3_BIT as c_int); - }, + } (Api::OpenGlEs, Some((2, _))) => { - if egl_version < &(1, 3) { return Err(CreationError::NoAvailablePixelFormat); } + if egl_version < &(1, 3) { + return Err(CreationError::NoAvailablePixelFormat); + } out.push(ffi::egl::RENDERABLE_TYPE as c_int); out.push(ffi::egl::OPENGL_ES2_BIT as c_int); out.push(ffi::egl::CONFORMANT as c_int); out.push(ffi::egl::OPENGL_ES2_BIT as c_int); - }, + } (Api::OpenGlEs, Some((1, _))) => { if egl_version >= &(1, 3) { out.push(ffi::egl::RENDERABLE_TYPE as c_int); @@ -552,15 +805,17 @@ unsafe fn choose_fbconfig(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisp out.push(ffi::egl::CONFORMANT as c_int); out.push(ffi::egl::OPENGL_ES_BIT as c_int); } - }, + } (Api::OpenGlEs, _) => unimplemented!(), (Api::OpenGl, _) => { - if egl_version < &(1, 3) { return Err(CreationError::NoAvailablePixelFormat); } + if egl_version < &(1, 3) { + return Err(CreationError::NoAvailablePixelFormat); + } out.push(ffi::egl::RENDERABLE_TYPE as c_int); out.push(ffi::egl::OPENGL_BIT as c_int); out.push(ffi::egl::CONFORMANT as c_int); out.push(ffi::egl::OPENGL_BIT as c_int); - }, + } (_, _) => unimplemented!(), }; @@ -610,6 +865,11 @@ unsafe fn choose_fbconfig(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisp return Err(CreationError::NoAvailablePixelFormat); } + if let Some(xid) = reqs.x11_visual_xid { + out.push(ffi::egl::NATIVE_VISUAL_ID as c_int); + out.push(xid as c_int); + } + // FIXME: srgb is not taken into account match reqs.release_behavior { @@ -617,7 +877,7 @@ unsafe fn choose_fbconfig(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisp ReleaseBehavior::None => { // TODO: with EGL you need to manually set the behavior unimplemented!() - }, + } } out.push(ffi::egl::NONE as c_int); @@ -627,7 +887,14 @@ unsafe fn choose_fbconfig(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisp // calling `eglChooseConfig` let mut config_id = mem::uninitialized(); let mut num_configs = mem::uninitialized(); - if egl.ChooseConfig(display, descriptor.as_ptr(), &mut config_id, 1, &mut num_configs) == 0 { + if egl.ChooseConfig( + display, + descriptor.as_ptr(), + &mut config_id, + 1, + &mut num_configs, + ) == 0 + { return Err(CreationError::OsError(format!("eglChooseConfig failed"))); } if num_configs == 0 { @@ -636,52 +903,73 @@ unsafe fn choose_fbconfig(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisp // analyzing each config macro_rules! attrib { - ($egl:expr, $display:expr, $config:expr, $attr:expr) => ( - { - let mut value = mem::uninitialized(); - let res = $egl.GetConfigAttrib($display, $config, - $attr as ffi::egl::types::EGLint, &mut value); - if res == 0 { - return Err(CreationError::OsError(format!("eglGetConfigAttrib failed"))); - } - value + ($egl:expr, $display:expr, $config:expr, $attr:expr) => {{ + let mut value = mem::uninitialized(); + let res = $egl.GetConfigAttrib( + $display, + $config, + $attr as ffi::egl::types::EGLint, + &mut value, + ); + if res == 0 { + return Err(CreationError::OsError(format!( + "eglGetConfigAttrib failed" + ))); } - ) + value + }}; }; let desc = PixelFormat { - hardware_accelerated: attrib!(egl, display, config_id, ffi::egl::CONFIG_CAVEAT) - != ffi::egl::SLOW_CONFIG as i32, - color_bits: attrib!(egl, display, config_id, ffi::egl::RED_SIZE) as u8 + - attrib!(egl, display, config_id, ffi::egl::BLUE_SIZE) as u8 + - attrib!(egl, display, config_id, ffi::egl::GREEN_SIZE) as u8, - alpha_bits: attrib!(egl, display, config_id, ffi::egl::ALPHA_SIZE) as u8, - depth_bits: attrib!(egl, display, config_id, ffi::egl::DEPTH_SIZE) as u8, - stencil_bits: attrib!(egl, display, config_id, ffi::egl::STENCIL_SIZE) as u8, + hardware_accelerated: attrib!( + egl, + display, + config_id, + ffi::egl::CONFIG_CAVEAT + ) != ffi::egl::SLOW_CONFIG as i32, + color_bits: attrib!(egl, display, config_id, ffi::egl::RED_SIZE) as u8 + + attrib!(egl, display, config_id, ffi::egl::BLUE_SIZE) as u8 + + attrib!(egl, display, config_id, ffi::egl::GREEN_SIZE) as u8, + alpha_bits: attrib!(egl, display, config_id, ffi::egl::ALPHA_SIZE) + as u8, + depth_bits: attrib!(egl, display, config_id, ffi::egl::DEPTH_SIZE) + as u8, + stencil_bits: attrib!(egl, display, config_id, ffi::egl::STENCIL_SIZE) + as u8, stereoscopy: false, double_buffer: true, - multisampling: match attrib!(egl, display, config_id, ffi::egl::SAMPLES) { + multisampling: match attrib!(egl, display, config_id, ffi::egl::SAMPLES) + { 0 | 1 => None, a => Some(a as u16), }, - srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that + srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that }; Ok((config_id, desc)) } -unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDisplay, - egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), - extensions: &[String], api: Api, version: (u8, u8), - config_id: ffi::egl::types::EGLConfig, gl_debug: bool, - gl_robustness: Robustness) - -> Result -{ +unsafe fn create_context( + display: ffi::egl::types::EGLDisplay, + egl_version: &(ffi::egl::types::EGLint, ffi::egl::types::EGLint), + extensions: &[String], + api: Api, + version: (u8, u8), + config_id: ffi::egl::types::EGLConfig, + gl_debug: bool, + gl_robustness: Robustness, + share: ffi::EGLContext, +) -> Result { + let egl = EGL.as_ref().unwrap(); + let mut context_attributes = Vec::with_capacity(10); let mut flags = 0; - if egl_version >= &(1, 5) || extensions.iter().find(|s| s == &"EGL_KHR_create_context") - .is_some() + if egl_version >= &(1, 5) + || extensions + .iter() + .find(|s| s == &"EGL_KHR_create_context") + .is_some() { context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32); context_attributes.push(version.0 as i32); @@ -689,60 +977,82 @@ unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDispl context_attributes.push(version.1 as i32); // handling robustness - let supports_robustness = egl_version >= &(1, 5) || - extensions.iter() - .find(|s| s == &"EGL_EXT_create_context_robustness") - .is_some(); + let supports_robustness = egl_version >= &(1, 5) + || extensions + .iter() + .find(|s| s == &"EGL_EXT_create_context_robustness") + .is_some(); match gl_robustness { Robustness::NotRobust => (), Robustness::NoError => { - if extensions.iter().find(|s| s == &"EGL_KHR_create_context_no_error").is_some() { - context_attributes.push(ffi::egl::CONTEXT_OPENGL_NO_ERROR_KHR as c_int); + if extensions + .iter() + .find(|s| s == &"EGL_KHR_create_context_no_error") + .is_some() + { + context_attributes + .push(ffi::egl::CONTEXT_OPENGL_NO_ERROR_KHR as c_int); context_attributes.push(1); } - }, + } Robustness::RobustNoResetNotification => { if supports_robustness { - context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY - as c_int); - context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as c_int); - flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int; + context_attributes.push( + ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY + as c_int, + ); + context_attributes + .push(ffi::egl::NO_RESET_NOTIFICATION as c_int); + flags = + flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int; } else { return Err(CreationError::RobustnessNotSupported); } - }, + } Robustness::TryRobustNoResetNotification => { if supports_robustness { - context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY - as c_int); - context_attributes.push(ffi::egl::NO_RESET_NOTIFICATION as c_int); - flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int; + context_attributes.push( + ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY + as c_int, + ); + context_attributes + .push(ffi::egl::NO_RESET_NOTIFICATION as c_int); + flags = + flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int; } - }, + } Robustness::RobustLoseContextOnReset => { if supports_robustness { - context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY - as c_int); - context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as c_int); - flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int; + context_attributes.push( + ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY + as c_int, + ); + context_attributes + .push(ffi::egl::LOSE_CONTEXT_ON_RESET as c_int); + flags = + flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int; } else { return Err(CreationError::RobustnessNotSupported); } - }, + } Robustness::TryRobustLoseContextOnReset => { if supports_robustness { - context_attributes.push(ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY - as c_int); - context_attributes.push(ffi::egl::LOSE_CONTEXT_ON_RESET as c_int); - flags = flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int; + context_attributes.push( + ffi::egl::CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY + as c_int, + ); + context_attributes + .push(ffi::egl::LOSE_CONTEXT_ON_RESET as c_int); + flags = + flags | ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int; } - }, + } } if gl_debug { @@ -752,27 +1062,29 @@ unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDispl } // TODO: using this flag sometimes generates an error - // there was a change in the specs that added this flag, so it may not be - // supported everywhere ; however it is not possible to know whether it is - // supported or not - //flags = flags | ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32; + // there was a change in the specs that added this flag, so it + // may not be supported everywhere ; however it is + // not possible to know whether it is supported or + // not flags = flags | + // ffi::egl::CONTEXT_OPENGL_DEBUG_BIT_KHR as i32; } - // In at least some configurations, the Android emulator’s GL implementation - // advertises support for the EGL_KHR_create_context extension - // but returns BAD_ATTRIBUTE when CONTEXT_FLAGS_KHR is used. + // In at least some configurations, the Android emulator’s GL + // implementation advertises support for the + // EGL_KHR_create_context extension but returns BAD_ATTRIBUTE + // when CONTEXT_FLAGS_KHR is used. if flags != 0 { 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 => { + Robustness::RobustNoResetNotification + | Robustness::RobustLoseContextOnReset => { return Err(CreationError::RobustnessNotSupported); - }, - _ => () + } + _ => (), } context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); @@ -781,13 +1093,18 @@ unsafe fn create_context(egl: &ffi::egl::Egl, display: ffi::egl::types::EGLDispl context_attributes.push(ffi::egl::NONE as i32); - let context = egl.CreateContext(display, config_id, ptr::null(), - context_attributes.as_ptr()); + let context = egl.CreateContext( + display, + config_id, + share, + context_attributes.as_ptr(), + ); if context.is_null() { match egl.GetError() as u32 { - ffi::egl::BAD_MATCH | - ffi::egl::BAD_ATTRIBUTE => return Err(CreationError::OpenGlVersionNotSupported), + ffi::egl::BAD_MATCH | ffi::egl::BAD_ATTRIBUTE => { + return Err(CreationError::OpenGlVersionNotSupported); + } e => panic!("eglCreateContext failed: 0x{:x}", e), } } diff --git a/src/api/glx/mod.rs b/src/api/glx/mod.rs index 21d9f041cc..a61d2f7cba 100644 --- a/src/api/glx/mod.rs +++ b/src/api/glx/mod.rs @@ -1,29 +1,27 @@ -#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] +#![cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] use { - Api, - ContextError, - CreationError, - GlAttributes, - GlProfile, - GlRequest, - PixelFormat, - PixelFormatRequirements, - ReleaseBehavior, - Robustness, + Api, ContextError, CreationError, GlAttributes, GlProfile, GlRequest, + PixelFormat, PixelFormatRequirements, ReleaseBehavior, Robustness, }; -use std::{mem, ptr, slice}; use std::ffi::{CStr, CString}; use std::sync::Arc; +use std::{mem, ptr, slice}; use libc::{self, c_int}; use winit::os::unix::x11::XConnection; pub mod ffi { - pub use x11_dl::xlib::*; pub use self::glx::types::GLXContext; + pub use x11_dl::xlib::*; /// GLX bindings pub mod glx { @@ -36,8 +34,55 @@ pub mod ffi { } } +mod glx { + use super::ffi; + use api::dlloader::{SymTrait, SymWrapper}; + use std::ops::{Deref, DerefMut}; + + #[derive(Clone)] + pub struct Glx(SymWrapper); + + /// Because `*const libc::c_void` doesn't implement `Sync`. + unsafe impl Sync for Glx {} + + impl SymTrait for ffi::glx::Glx { + fn load_with(loadfn: F) -> Self + where + F: FnMut(&'static str) -> *const std::os::raw::c_void, + { + Self::load_with(loadfn) + } + } + + impl Glx { + pub fn new() -> Result { + let paths = vec!["libGLX.so.1", "libGLX.so"]; + + SymWrapper::new(paths).map(|i| Glx(i)) + } + } + + impl Deref for Glx { + type Target = ffi::glx::Glx; + + fn deref(&self) -> &ffi::glx::Glx { + &self.0 + } + } + + impl DerefMut for Glx { + fn deref_mut(&mut self) -> &mut ffi::glx::Glx { + &mut self.0 + } + } +} +pub use self::glx::Glx; + +lazy_static! { + pub static ref GLX: Option = Glx::new().ok(); +} + pub struct Context { - glx: ffi::glx::Glx, xconn: Arc, window: ffi::Window, context: ffi::GLXContext, @@ -46,21 +91,21 @@ pub struct Context { impl Context { pub fn new<'a>( - glx: ffi::glx::Glx, xconn: Arc, pf_reqs: &PixelFormatRequirements, opengl: &'a GlAttributes<&'a Context>, screen_id: libc::c_int, transparent: bool, - ) -> Result, CreationError> - { - // This is completely ridiculous, but VirtualBox's OpenGL driver needs some call handled by - // *it* (i.e. not Mesa) to occur before anything else can happen. That is because - // VirtualBox's OpenGL driver is going to apply binary patches to Mesa in the DLL + ) -> Result, CreationError> { + let glx = GLX.as_ref().unwrap(); + // This is completely ridiculous, but VirtualBox's OpenGL driver needs + // some call handled by *it* (i.e. not Mesa) to occur before + // anything else can happen. That is because VirtualBox's OpenGL + // driver is going to apply binary patches to Mesa in the DLL // constructor and until it's loaded it won't have a chance to do that. // - // The easiest way to do this is to just call `glXQueryVersion()` before doing anything - // else. See: https://www.virtualbox.org/ticket/8293 + // The easiest way to do this is to just call `glXQueryVersion()` before + // doing anything else. See: https://www.virtualbox.org/ticket/8293 let (mut major, mut minor) = (0, 0); unsafe { glx.QueryVersion(xconn.display as *mut _, &mut major, &mut minor); @@ -68,9 +113,12 @@ impl Context { // loading the list of extensions let extensions = unsafe { - let extensions = glx.QueryExtensionsString(xconn.display as *mut _, screen_id); + let extensions = + glx.QueryExtensionsString(xconn.display as *mut _, screen_id); if extensions.is_null() { - return Err(CreationError::OsError(format!("`glXQueryExtensionsString` found no glX extensions"))); + return Err(CreationError::OsError(format!( + "`glXQueryExtensionsString` found no glX extensions" + ))); } let extensions = CStr::from_ptr(extensions).to_bytes().to_vec(); String::from_utf8(extensions).unwrap() @@ -86,16 +134,18 @@ impl Context { screen_id, pf_reqs, transparent, - ).map_err(|_| CreationError::NoAvailablePixelFormat)? + ) + .map_err(|_| CreationError::NoAvailablePixelFormat)? }; // getting the visual infos let visual_infos: ffi::glx::types::XVisualInfo = unsafe { - let vi = glx.GetVisualFromFBConfig(xconn.display as *mut _, fb_config); + let vi = + glx.GetVisualFromFBConfig(xconn.display as *mut _, fb_config); if vi.is_null() { - return Err(CreationError::OsError( - format!("`glXGetVisualFromFBConfig` failed: invalid `GLXFBConfig`") - )); + return Err(CreationError::OsError(format!( + "`glXGetVisualFromFBConfig` failed: invalid `GLXFBConfig`" + ))); } let vi_copy = ptr::read(vi as *const _); (xconn.xlib.XFree)(vi as *mut _); @@ -103,7 +153,6 @@ impl Context { }; Ok(ContextPrototype { - glx, extensions, xconn, opengl, @@ -114,10 +163,18 @@ impl Context { } pub unsafe fn make_current(&self) -> Result<(), ContextError> { - let res = self.glx.MakeCurrent(self.xconn.display as *mut _, self.window, self.context); + let glx = GLX.as_ref().unwrap(); + let res = glx.MakeCurrent( + self.xconn.display as *mut _, + self.window, + self.context, + ); if res == 0 { let err = self.xconn.check_errors(); - Err(ContextError::OsError(format!("`glXMakeCurrent` failed: {:?}", err))) + Err(ContextError::OsError(format!( + "`glXMakeCurrent` failed: {:?}", + err + ))) } else { Ok(()) } @@ -125,22 +182,28 @@ impl Context { #[inline] pub fn is_current(&self) -> bool { - unsafe { self.glx.GetCurrentContext() == self.context } + let glx = GLX.as_ref().unwrap(); + unsafe { glx.GetCurrentContext() == self.context } } pub fn get_proc_address(&self, addr: &str) -> *const () { + let glx = GLX.as_ref().unwrap(); let addr = CString::new(addr.as_bytes()).unwrap(); let addr = addr.as_ptr(); - unsafe { - self.glx.GetProcAddress(addr as *const _) as *const _ - } + unsafe { glx.GetProcAddress(addr as *const _) as *const _ } } #[inline] pub fn swap_buffers(&self) -> Result<(), ContextError> { - unsafe { self.glx.SwapBuffers(self.xconn.display as *mut _, self.window); } + let glx = GLX.as_ref().unwrap(); + unsafe { + glx.SwapBuffers(self.xconn.display as *mut _, self.window); + } if let Err(err) = self.xconn.check_errors() { - Err(ContextError::OsError(format!("`glXSwapBuffers` failed: {:?}", err))) + Err(ContextError::OsError(format!( + "`glXSwapBuffers` failed: {:?}", + err + ))) } else { Ok(()) } @@ -167,18 +230,22 @@ unsafe impl Sync for Context {} impl Drop for Context { fn drop(&mut self) { + let glx = GLX.as_ref().unwrap(); unsafe { if self.is_current() { - self.glx.MakeCurrent(self.xconn.display as *mut _, 0, ptr::null_mut()); + glx.MakeCurrent( + self.xconn.display as *mut _, + 0, + ptr::null_mut(), + ); } - self.glx.DestroyContext(self.xconn.display as *mut _, self.context); + glx.DestroyContext(self.xconn.display as *mut _, self.context); } } } pub struct ContextPrototype<'a> { - glx: ffi::glx::Glx, extensions: String, xconn: Arc, opengl: &'a GlAttributes<&'a Context>, @@ -194,111 +261,164 @@ impl<'a> ContextPrototype<'a> { } pub fn finish(self, window: ffi::Window) -> Result { + let glx = GLX.as_ref().unwrap(); let share = match self.opengl.sharing { - Some(ctxt) => ctxt.context, - None => ptr::null() + Some(ctx) => ctx.context, + None => ptr::null(), }; // loading the extra GLX functions let extra_functions = ffi::glx_extra::Glx::load_with(|proc_name| { let c_str = CString::new(proc_name).unwrap(); - unsafe { self.glx.GetProcAddress(c_str.as_ptr() as *const u8) as *const _ } + unsafe { + glx.GetProcAddress(c_str.as_ptr() as *const u8) as *const _ + } }); // creating GL context let context = match self.opengl.version { GlRequest::Latest => { - let opengl_versions = [(4, 6), (4, 5), (4, 4), (4, 3), (4, 2), (4, 1), (4, 0), - (3, 3), (3, 2), (3, 1)]; - let ctxt; - 'outer: loop - { - // Try all OpenGL versions in descending order because some non-compliant - // drivers don't return the latest supported version but the one requested - for opengl_version in opengl_versions.iter() - { - match create_context(&self.glx, &extra_functions, &self.extensions, &self.xconn.xlib, - *opengl_version, self.opengl.profile, - self.opengl.debug, self.opengl.robustness, share, - self.xconn.display, self.fb_config, &self.visual_infos) - { + let opengl_versions = [ + (4, 6), + (4, 5), + (4, 4), + (4, 3), + (4, 2), + (4, 1), + (4, 0), + (3, 3), + (3, 2), + (3, 1), + ]; + let ctx; + 'outer: loop { + // Try all OpenGL versions in descending order because some + // non-compliant drivers don't return + // the latest supported version but the one requested + for opengl_version in opengl_versions.iter() { + match create_context( + &extra_functions, + &self.extensions, + &self.xconn.xlib, + *opengl_version, + self.opengl.profile, + self.opengl.debug, + self.opengl.robustness, + share, + self.xconn.display, + self.fb_config, + &self.visual_infos, + ) { Ok(x) => { - ctxt = x; + ctx = x; break 'outer; - }, - Err(_) => continue + } + Err(_) => continue, } } - ctxt = create_context(&self.glx, &extra_functions, &self.extensions, &self.xconn.xlib, (1, 0), - self.opengl.profile, self.opengl.debug, - self.opengl.robustness, share, - self.xconn.display, self.fb_config, &self.visual_infos)?; + ctx = create_context( + &extra_functions, + &self.extensions, + &self.xconn.xlib, + (1, 0), + self.opengl.profile, + self.opengl.debug, + self.opengl.robustness, + share, + self.xconn.display, + self.fb_config, + &self.visual_infos, + )?; break; } - ctxt - }, - GlRequest::Specific(Api::OpenGl, (major, minor)) => { - create_context(&self.glx, &extra_functions, &self.extensions, &self.xconn.xlib, (major, minor), - self.opengl.profile, self.opengl.debug, - self.opengl.robustness, share, self.xconn.display, self.fb_config, - &self.visual_infos)? - }, + ctx + } + GlRequest::Specific(Api::OpenGl, (major, minor)) => create_context( + &extra_functions, + &self.extensions, + &self.xconn.xlib, + (major, minor), + self.opengl.profile, + self.opengl.debug, + self.opengl.robustness, + share, + self.xconn.display, + self.fb_config, + &self.visual_infos, + )?, GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"), - GlRequest::GlThenGles { opengl_version: (major, minor), .. } => { - create_context(&self.glx, &extra_functions, &self.extensions, &self.xconn.xlib, (major, minor), - self.opengl.profile, self.opengl.debug, - self.opengl.robustness, share, self.xconn.display, self.fb_config, - &self.visual_infos)? - }, + GlRequest::GlThenGles { + opengl_version: (major, minor), + .. + } => create_context( + &extra_functions, + &self.extensions, + &self.xconn.xlib, + (major, minor), + self.opengl.profile, + self.opengl.debug, + self.opengl.robustness, + share, + self.xconn.display, + self.fb_config, + &self.visual_infos, + )?, }; // vsync if self.opengl.vsync { - unsafe { self.glx.MakeCurrent(self.xconn.display as *mut _, window, context) }; + unsafe { + glx + .MakeCurrent(self.xconn.display as *mut _, window, context) + }; - if check_ext(&self.extensions, "GLX_EXT_swap_control") && extra_functions.SwapIntervalEXT.is_loaded() { + if check_ext(&self.extensions, "GLX_EXT_swap_control") + && extra_functions.SwapIntervalEXT.is_loaded() + { // this should be the most common extension unsafe { extra_functions.SwapIntervalEXT(self.xconn.display as *mut _, window, 1); } - // checking that it worked - // TODO: handle this - /*if self.builder.strict { - let mut swap = unsafe { mem::uninitialized() }; - unsafe { - self.glx.QueryDrawable(self.xconn.display as *mut _, window, - ffi::glx_extra::SWAP_INTERVAL_EXT as i32, - &mut swap); - } + // checking that it worked + // TODO: handle this + /*if self.builder.strict { + let mut swap = unsafe { mem::uninitialized() }; + unsafe { + glx.QueryDrawable(self.xconn.display as *mut _, window, + ffi::glx_extra::SWAP_INTERVAL_EXT as i32, + &mut swap); + } - if swap != 1 { - return Err(CreationError::OsError(format!("Couldn't setup vsync: expected \ - interval `1` but got `{}`", swap))); - } - }*/ + if swap != 1 { + return Err(CreationError::OsError(format!("Couldn't setup vsync: expected \ + interval `1` but got `{}`", swap))); + } + }*/ // GLX_MESA_swap_control is not official /*} else if extra_functions.SwapIntervalMESA.is_loaded() { - unsafe { - extra_functions.SwapIntervalMESA(1); - }*/ - - } else if check_ext(&self.extensions, "GLX_SGI_swap_control") && extra_functions.SwapIntervalSGI.is_loaded() { + unsafe { + extra_functions.SwapIntervalMESA(1); + }*/ + } else if check_ext(&self.extensions, "GLX_SGI_swap_control") + && extra_functions.SwapIntervalSGI.is_loaded() + { unsafe { extra_functions.SwapIntervalSGI(1); } - - }/* else if self.builder.strict { - // TODO: handle this - return Err(CreationError::OsError(format!("Couldn't find any available vsync extension"))); - }*/ - - unsafe { self.glx.MakeCurrent(self.xconn.display as *mut _, 0, ptr::null()) }; + } /* else if self.builder.strict { + // TODO: handle this + return Err(CreationError::OsError(format!("Couldn't find any available vsync extension"))); + }*/ + + unsafe { + glx + .MakeCurrent(self.xconn.display as *mut _, 0, ptr::null()) + }; } Ok(Context { - glx: self.glx, xconn: self.xconn, window, context, @@ -307,19 +427,27 @@ impl<'a> ContextPrototype<'a> { } } -extern fn x_error_callback(_dpy: *mut ffi::Display, _err: *mut ffi::XErrorEvent) -> i32 -{ +extern "C" fn x_error_callback( + _dpy: *mut ffi::Display, + _err: *mut ffi::XErrorEvent, +) -> i32 { 0 } - -fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, extensions: &str, xlib: &ffi::Xlib, - version: (u8, u8), profile: Option, debug: bool, - robustness: Robustness, share: ffi::GLXContext, display: *mut ffi::Display, - fb_config: ffi::glx::types::GLXFBConfig, - visual_infos: &ffi::XVisualInfo) - -> Result -{ +fn create_context( + extra_functions: &ffi::glx_extra::Glx, + extensions: &str, + xlib: &ffi::Xlib, + version: (u8, u8), + profile: Option, + debug: bool, + robustness: Robustness, + share: ffi::GLXContext, + display: *mut ffi::Display, + fb_config: ffi::glx::types::GLXFBConfig, + visual_infos: &ffi::XVisualInfo, +) -> Result { + let glx = GLX.as_ref().unwrap(); unsafe { let old_callback = (xlib.XSetErrorHandler)(Some(x_error_callback)); let context = if check_ext(extensions, "GLX_ARB_create_context") { @@ -332,13 +460,16 @@ fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, ex if let Some(profile) = profile { let flag = match profile { - GlProfile::Compatibility => - ffi::glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, - GlProfile::Core => - ffi::glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB, + GlProfile::Compatibility => { + ffi::glx_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB + } + GlProfile::Core => { + ffi::glx_extra::CONTEXT_CORE_PROFILE_BIT_ARB + } }; - attributes.push(ffi::glx_extra::CONTEXT_PROFILE_MASK_ARB as c_int); + attributes + .push(ffi::glx_extra::CONTEXT_PROFILE_MASK_ARB as c_int); attributes.push(flag as c_int); } @@ -348,30 +479,48 @@ fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, ex // robustness if check_ext(extensions, "GLX_ARB_create_context_robustness") { match robustness { - Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => { - attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int); - attributes.push(ffi::glx_extra::NO_RESET_NOTIFICATION_ARB as c_int); - flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int; - }, - Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => { - attributes.push(ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int); - attributes.push(ffi::glx_extra::LOSE_CONTEXT_ON_RESET_ARB as c_int); - flags = flags | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int; - }, + Robustness::RobustNoResetNotification + | Robustness::TryRobustNoResetNotification => { + attributes.push( + ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int, + ); + attributes.push( + ffi::glx_extra::NO_RESET_NOTIFICATION_ARB + as c_int, + ); + flags = flags + | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB + as c_int; + } + Robustness::RobustLoseContextOnReset + | Robustness::TryRobustLoseContextOnReset => { + attributes.push( + ffi::glx_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int, + ); + attributes.push( + ffi::glx_extra::LOSE_CONTEXT_ON_RESET_ARB + as c_int, + ); + flags = flags + | ffi::glx_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB + as c_int; + } Robustness::NotRobust => (), Robustness::NoError => (), } } else { match robustness { - Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + Robustness::RobustNoResetNotification + | Robustness::RobustLoseContextOnReset => { return Err(CreationError::RobustnessNotSupported); - }, - _ => () + } + _ => (), } } if debug { - flags = flags | ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as c_int; + flags = + flags | ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as c_int; } flags @@ -382,19 +531,30 @@ fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, ex attributes.push(0); - extra_functions.CreateContextAttribsARB(display as *mut _, fb_config, share, 1, - attributes.as_ptr()) - + extra_functions.CreateContextAttribsARB( + display as *mut _, + fb_config, + share, + 1, + attributes.as_ptr(), + ) } else { let visual_infos: *const ffi::XVisualInfo = visual_infos; - glx.CreateContext(display as *mut _, visual_infos as *mut _, share, 1) + glx.CreateContext( + display as *mut _, + visual_infos as *mut _, + share, + 1, + ) }; (xlib.XSetErrorHandler)(old_callback); if context.is_null() { // TODO: check for errors and return `OpenGlVersionNotSupported` - return Err(CreationError::OsError(format!("GL context creation failed"))); + return Err(CreationError::OsError(format!( + "GL context creation failed" + ))); } Ok(context) @@ -402,20 +562,31 @@ fn create_context(glx: &ffi::glx::Glx, extra_functions: &ffi::glx_extra::Glx, ex } /// Enumerates all available FBConfigs -unsafe fn choose_fbconfig(glx: &ffi::glx::Glx, extensions: &str, xlib: &ffi::Xlib, - display: *mut ffi::Display, screen_id: libc::c_int, - reqs: &PixelFormatRequirements, transparent: bool) - -> Result<(ffi::glx::types::GLXFBConfig, PixelFormat), ()> -{ +unsafe fn choose_fbconfig( + glx: &Glx, + extensions: &str, + xlib: &ffi::Xlib, + display: *mut ffi::Display, + screen_id: libc::c_int, + reqs: &PixelFormatRequirements, + transparent: bool, +) -> Result<(ffi::glx::types::GLXFBConfig, PixelFormat), ()> { let descriptor = { let mut out: Vec = Vec::with_capacity(37); out.push(ffi::glx::X_RENDERABLE as c_int); out.push(1); + // TODO: If passed an visual xid, maybe we should stop assuming + // TRUE_COLOR. out.push(ffi::glx::X_VISUAL_TYPE as c_int); out.push(ffi::glx::TRUE_COLOR as c_int); + if let Some(xid) = reqs.x11_visual_xid { + out.push(ffi::glx::VISUAL_ID as c_int); + out.push(xid as c_int); + } + out.push(ffi::glx::DRAWABLE_TYPE as c_int); out.push(ffi::glx::WINDOW_BIT as c_int); @@ -488,10 +659,15 @@ unsafe fn choose_fbconfig(glx: &ffi::glx::Glx, extensions: &str, xlib: &ffi::Xli ReleaseBehavior::Flush => (), ReleaseBehavior::None => { if check_ext(extensions, "GLX_ARB_context_flush_control") { - out.push(ffi::glx_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int); - out.push(ffi::glx_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as c_int); + out.push( + ffi::glx_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int, + ); + out.push( + ffi::glx_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB + as c_int, + ); } - }, + } } out.push(ffi::glx::CONFIG_CAVEAT as c_int); @@ -504,16 +680,25 @@ unsafe fn choose_fbconfig(glx: &ffi::glx::Glx, extensions: &str, xlib: &ffi::Xli // calling glXChooseFBConfig let fb_config = { let mut num_configs = 1; - let configs = glx.ChooseFBConfig(display as *mut _, screen_id, descriptor.as_ptr(), - &mut num_configs); - if configs.is_null() { return Err(()); } - if num_configs == 0 { return Err(()); } + let configs = glx.ChooseFBConfig( + display as *mut _, + screen_id, + descriptor.as_ptr(), + &mut num_configs, + ); + if configs.is_null() { + return Err(()); + } + if num_configs == 0 { + return Err(()); + } let config = if transparent { let configs = slice::from_raw_parts(configs, num_configs as usize); configs.iter().find(|&config| { let vi = glx.GetVisualFromFBConfig(display as *mut _, *config); - // Transparency was requested, so only choose configs with 32 bits for RGBA. + // Transparency was requested, so only choose configs with 32 + // bits for RGBA. let found = !vi.is_null() && (*vi).depth == 32; (xlib.XFree)(vi as *mut _); @@ -541,11 +726,11 @@ unsafe fn choose_fbconfig(glx: &ffi::glx::Glx, extensions: &str, xlib: &ffi::Xli }; let pf_desc = PixelFormat { - hardware_accelerated: get_attrib(ffi::glx::CONFIG_CAVEAT as c_int) != - ffi::glx::SLOW_CONFIG as c_int, - color_bits: get_attrib(ffi::glx::RED_SIZE as c_int) as u8 + - get_attrib(ffi::glx::GREEN_SIZE as c_int) as u8 + - get_attrib(ffi::glx::BLUE_SIZE as c_int) as u8, + hardware_accelerated: get_attrib(ffi::glx::CONFIG_CAVEAT as c_int) + != ffi::glx::SLOW_CONFIG as c_int, + color_bits: get_attrib(ffi::glx::RED_SIZE as c_int) as u8 + + get_attrib(ffi::glx::GREEN_SIZE as c_int) as u8 + + get_attrib(ffi::glx::BLUE_SIZE as c_int) as u8, alpha_bits: get_attrib(ffi::glx::ALPHA_SIZE as c_int) as u8, depth_bits: get_attrib(ffi::glx::DEPTH_SIZE as c_int) as u8, stencil_bits: get_attrib(ffi::glx::STENCIL_SIZE as c_int) as u8, @@ -556,8 +741,11 @@ unsafe fn choose_fbconfig(glx: &ffi::glx::Glx, extensions: &str, xlib: &ffi::Xli } else { None }, - srgb: get_attrib(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) != 0 || - get_attrib(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int) != 0, + srgb: get_attrib(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int) + != 0 + || get_attrib( + ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int, + ) != 0, }; Ok((fb_config, pf_desc)) diff --git a/src/api/ios/ffi.rs b/src/api/ios/ffi.rs index d971c0c89d..e8381518c6 100644 --- a/src/api/ios/ffi.rs +++ b/src/api/ios/ffi.rs @@ -2,8 +2,8 @@ use std::os::raw::*; -use objc::{Encode, Encoding}; use objc::runtime::Object; +use objc::{Encode, Encoding}; pub mod gles { include!(concat!(env!("OUT_DIR"), "/gles2_bindings.rs")); @@ -81,7 +81,7 @@ pub const kEAGLRenderingAPIOpenGLES3: NSUInteger = 3; #[link(name = "UIKit", kind = "framework")] #[link(name = "CoreFoundation", kind = "framework")] #[link(name = "GlKit", kind = "framework")] -extern { +extern "C" { pub static kEAGLColorFormatRGB565: id; // pub static kEAGLColorFormatRGBA8: id; pub static kEAGLDrawablePropertyColorFormat: id; @@ -91,7 +91,7 @@ extern { pub const RTLD_LAZY: c_int = 0x001; pub const RTLD_GLOBAL: c_int = 0x100; -extern { +extern "C" { pub fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void; pub fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void; } diff --git a/src/api/ios/mod.rs b/src/api/ios/mod.rs index 8f7f4ade90..f4c16f5891 100644 --- a/src/api/ios/mod.rs +++ b/src/api/ios/mod.rs @@ -26,10 +26,10 @@ //! fn start_inner() { //! ... //! } -//! //! ``` //! -//! Compile project and then drag resulting .a into Xcode project. Add glutin.h to xcode. +//! Compile project and then drag resulting .a into Xcode project. Add glutin.h +//! to xcode. //! //! ```c //! void start_glutin_app(); @@ -52,38 +52,32 @@ //! - applicationWillEnterForeground is Suspended(false) //! - applicationWillTerminate is Destroyed //! -//! Keep in mind that after Destroyed event is received every attempt to draw with opengl will result in segfault. +//! Keep in mind that after Destroyed event is received every attempt to draw +//! with opengl will result in segfault. //! -//! Also note that app will not receive Destroyed event if suspended, it will be SIGKILL'ed +//! Also note that app will not receive Destroyed event if suspended, it will be +//! SIGKILL'ed #![cfg(target_os = "ios")] -use std::io; use std::ffi::CString; +use std::io; use std::mem; use std::os::raw::*; use objc::declare::ClassDecl; -use objc::runtime::{BOOL, Class, NO, Object, Sel, YES}; +use objc::runtime::{Class, Object, Sel, BOOL, NO, YES}; +use os::ios::{WindowBuilderExt, WindowExt}; +use os::ContextTraitExt; use { - Api, - ContextError, - CreationError, - EventsLoop, - GlAttributes, - GlRequest, - PixelFormat, - PixelFormatRequirements, - Window, - WindowBuilder, + Api, ContextError, CreationError, EventsLoop, GlAttributes, GlRequest, + PixelFormat, PixelFormatRequirements, Window, WindowBuilder, }; -use os::GlContextExt; -use os::ios::{WindowExt, WindowBuilderExt}; mod ffi; -use self::ffi::*; pub use self::ffi::id; +use self::ffi::*; #[derive(Debug, PartialEq)] enum ColorFormat { @@ -95,8 +89,9 @@ enum ColorFormat { impl ColorFormat { #[allow(non_upper_case_globals)] pub fn for_view(view: id) -> Self { - let color_format: NSUInteger = unsafe { msg_send![view, drawableColorFormat] }; - match color_format{ + let color_format: NSUInteger = + unsafe { msg_send![view, drawableColorFormat] }; + match color_format { GLKViewDrawableColorFormatRGBA8888 => ColorFormat::Rgba8888, GLKViewDrawableColorFormatRGB565 => ColorFormat::Rgb565, GLKViewDrawableColorFormatSRGBA8888 => ColorFormat::Srgba8888, @@ -127,7 +122,8 @@ impl ColorFormat { #[allow(non_upper_case_globals)] fn depth_for_view(view: id) -> u8 { - let depth_format: NSUInteger = unsafe { msg_send![view, drawableDepthFormat] }; + let depth_format: NSUInteger = + unsafe { msg_send![view, drawableDepthFormat] }; match depth_format { GLKViewDrawableDepthFormatNone => 0, GLKViewDrawableDepthFormat16 => 16, @@ -138,7 +134,8 @@ fn depth_for_view(view: id) -> u8 { #[allow(non_upper_case_globals)] fn stencil_for_view(view: id) -> u8 { - let stencil_format: NSUInteger = unsafe { msg_send![view, drawableStencilFormat] }; + let stencil_format: NSUInteger = + unsafe { msg_send![view, drawableStencilFormat] }; match stencil_format { GLKViewDrawableStencilFormatNone => 0, GLKViewDrawableStencilFormat8 => 8, @@ -163,7 +160,9 @@ pub struct Context { fn validate_version(version: u8) -> Result { let version = version as NSUInteger; - if version >= kEAGLRenderingAPIOpenGLES1 && version <= kEAGLRenderingAPIOpenGLES3 { + if version >= kEAGLRenderingAPIOpenGLES1 + && version <= kEAGLRenderingAPIOpenGLES3 + { Ok(version) } else { Err(CreationError::OsError(format!( @@ -174,6 +173,7 @@ fn validate_version(version: u8) -> Result { } impl Context { + #[inline] pub fn new( builder: WindowBuilder, event_loop: &EventsLoop, @@ -181,22 +181,29 @@ impl Context { gl_attrs: &GlAttributes<&Context>, ) -> Result<(Window, Self), CreationError> { create_view_class(); - let view_class = Class::get("MainGLView").expect("Failed to get class `MainGLView`"); - let builder = builder.with_root_view_class(view_class as *const _ as *const _); - if gl_attrs.sharing.is_some() { unimplemented!("Shared contexts are unimplemented on iOS."); } + let view_class = + Class::get("MainGLView").expect("Failed to get class `MainGLView`"); + let builder = + builder.with_root_view_class(view_class as *const _ as *const _); + if gl_attrs.sharing.is_some() { + unimplemented!("Shared contexts are unimplemented on iOS."); + } let version = match gl_attrs.version { GlRequest::Latest => kEAGLRenderingAPIOpenGLES3, - GlRequest::Specific(api, (major, _minor)) => if api == Api::OpenGlEs { - validate_version(major)? - } else { - return Err(CreationError::OsError(format!( + GlRequest::Specific(api, (major, _minor)) => { + if api == Api::OpenGlEs { + validate_version(major)? + } else { + return Err(CreationError::OsError(format!( "Specified API ({:?}) is not availble on iOS. Only `Api::OpenGlEs` can be used", api, ))); - }, - GlRequest::GlThenGles { opengles_version: (major, _minor), .. } => { - validate_version(major)? - }, + } + } + GlRequest::GlThenGles { + opengles_version: (major, _minor), + .. + } => validate_version(major)?, }; let window = builder.build(event_loop)?; let context = unsafe { @@ -209,35 +216,52 @@ impl Context { Ok((window, context)) } + #[inline] pub fn new_context( el: &EventsLoop, pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Context>, - _shareable_with_windowed_contexts: bool, ) -> Result { let wb = WindowBuilder::new().with_visibility(false); - Self::new(wb, el, pf_reqs, gl_attr) - .map(|(_window, context)| context) + Self::new(wb, el, pf_reqs, gl_attr).map(|(_window, context)| context) + } + + /// See the docs in the crate root file. + #[inline] + pub fn new_separated( + _window: &Window, + _el: &EventsLoop, + _pf_reqs: &PixelFormatRequirements, + _gl_attr: &GlAttributes<&Context>, + ) -> Result { + unimplemented!() } - unsafe fn create_context(mut version: NSUInteger) -> Result { - let context_class = Class::get("EAGLContext").expect("Failed to get class `EAGLContext`"); + unsafe fn create_context( + mut version: NSUInteger, + ) -> Result { + let context_class = Class::get("EAGLContext") + .expect("Failed to get class `EAGLContext`"); let eagl_context: id = msg_send![context_class, alloc]; let mut valid_context = nil; while valid_context == nil && version > 0 { - valid_context = msg_send![eagl_context, initWithAPI:version]; + valid_context = msg_send![eagl_context, initWithAPI: version]; version -= 1; } if valid_context == nil { - Err(CreationError::OsError(format!("Failed to create an OpenGL ES context with any version"))) + Err(CreationError::OsError(format!( + "Failed to create an OpenGL ES context with any version" + ))) } else { Ok(eagl_context) } } unsafe fn init_context(&mut self, window: &Window) { - let dict_class = Class::get("NSDictionary").expect("Failed to get class `NSDictionary`"); - let number_class = Class::get("NSNumber").expect("Failed to get class `NSNumber`"); + let dict_class = Class::get("NSDictionary") + .expect("Failed to get class `NSDictionary`"); + let number_class = + Class::get("NSNumber").expect("Failed to get class `NSNumber`"); let draw_props: id = msg_send![dict_class, alloc]; let draw_props: id = msg_send![draw_props, initWithObjects: @@ -256,12 +280,14 @@ impl Context { let view = self.view; let scale_factor = window.get_hidpi_factor() as CGFloat; - let _: () = msg_send![view, setContentScaleFactor:scale_factor]; + let _: () = msg_send![view, setContentScaleFactor: scale_factor]; let layer: id = msg_send![view, layer]; - let _: () = msg_send![layer, setContentsScale:scale_factor]; - let _: () = msg_send![layer, setDrawableProperties:draw_props]; + let _: () = msg_send![layer, setContentsScale: scale_factor]; + let _: () = msg_send![layer, setDrawableProperties: draw_props]; - let gl = gles::Gles2::load_with(|symbol| self.get_proc_address(symbol) as *const c_void); + let gl = gles::Gles2::load_with(|symbol| { + self.get_proc_address(symbol) as *const c_void + }); let mut color_render_buf: gles::types::GLuint = 0; let mut frame_buf: gles::types::GLuint = 0; gl.GenRenderbuffers(1, &mut color_render_buf); @@ -275,10 +301,17 @@ impl Context { gl.GenFramebuffers(1, &mut frame_buf); gl.BindFramebuffer(gles::FRAMEBUFFER, frame_buf); - gl.FramebufferRenderbuffer(gles::FRAMEBUFFER, gles::COLOR_ATTACHMENT0, gles::RENDERBUFFER, color_render_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 { + if gl.CheckFramebufferStatus(gles::FRAMEBUFFER) + != gles::FRAMEBUFFER_COMPLETE + { panic!("framebuffer status: {:?}", status); } } @@ -286,13 +319,17 @@ impl Context { #[inline] pub fn swap_buffers(&self) -> Result<(), ContextError> { unsafe { - let res: BOOL = msg_send![self.eagl_context, presentRenderbuffer:gles::RENDERBUFFER]; + let res: BOOL = msg_send![ + self.eagl_context, + presentRenderbuffer: gles::RENDERBUFFER + ]; if res == YES { Ok(()) } else { - Err(ContextError::IoError( - io::Error::new(io::ErrorKind::Other, "`EAGLContext presentRenderbuffer` failed") - )) + Err(ContextError::IoError(io::Error::new( + io::ErrorKind::Other, + "`EAGLContext presentRenderbuffer` failed", + ))) } } } @@ -320,32 +357,38 @@ impl Context { #[inline] pub unsafe fn make_current(&self) -> Result<(), ContextError> { - let context_class = Class::get("EAGLContext").expect("Failed to get class `EAGLContext`"); - let res: BOOL = msg_send![context_class, setCurrentContext: self.eagl_context]; + let context_class = Class::get("EAGLContext") + .expect("Failed to get class `EAGLContext`"); + let res: BOOL = + msg_send![context_class, setCurrentContext: self.eagl_context]; if res == YES { Ok(()) } else { - Err(ContextError::IoError( - io::Error::new(io::ErrorKind::Other, "`EAGLContext setCurrentContext` failed") - )) + Err(ContextError::IoError(io::Error::new( + io::ErrorKind::Other, + "`EAGLContext setCurrentContext` failed", + ))) } } #[inline] pub fn is_current(&self) -> bool { - // TODO: This can likely be implemented using `currentContext`/`getCurrentContext` + // TODO: This can likely be implemented using + // `currentContext`/`getCurrentContext` true } #[inline] pub fn get_proc_address(&self, proc_name: &str) -> *const () { - let proc_name_c = CString::new(proc_name).expect("proc name contained interior nul byte"); + let proc_name_c = CString::new(proc_name) + .expect("proc name contained interior nul byte"); let path = b"/System/Library/Frameworks/OpenGLES.framework/OpenGLES\0"; let addr = unsafe { - let lib = dlopen(path.as_ptr() as *const c_char, RTLD_LAZY | RTLD_GLOBAL); + let lib = + dlopen(path.as_ptr() as *const c_char, RTLD_LAZY | RTLD_GLOBAL); dlsym(lib, proc_name_c.as_ptr()) as *const _ }; - //debug!("proc {} -> {:?}", proc_name, addr); + // debug!("proc {} -> {:?}", proc_name, addr); addr } @@ -356,30 +399,45 @@ impl Context { } fn create_view_class() { - extern fn init_with_frame(this: &Object, _: Sel, frame: CGRect) -> id { + extern "C" fn init_with_frame(this: &Object, _: Sel, frame: CGRect) -> id { unsafe { - let view: id = msg_send![super(this, class!(GLKView)), initWithFrame:frame]; + let view: id = + msg_send![super(this, class!(GLKView)), initWithFrame: frame]; - let mask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - let _: () = msg_send![view, setAutoresizingMask:mask]; - let _: () = msg_send![view, setAutoresizesSubviews:YES]; + let mask = UIViewAutoresizingFlexibleWidth + | UIViewAutoresizingFlexibleHeight; + let _: () = msg_send![view, setAutoresizingMask: mask]; + let _: () = msg_send![view, setAutoresizesSubviews: YES]; let layer: id = msg_send![view, layer]; - let _ : () = msg_send![layer, setOpaque:YES]; + let _: () = msg_send![layer, setOpaque: YES]; view } } - extern fn layer_class(_: &Class, _: Sel) -> *const Class { - unsafe { mem::transmute(Class::get("CAEAGLLayer").expect("Failed to get class `CAEAGLLayer`")) } + extern "C" fn layer_class(_: &Class, _: Sel) -> *const Class { + unsafe { + mem::transmute( + Class::get("CAEAGLLayer") + .expect("Failed to get class `CAEAGLLayer`"), + ) + } } - let superclass = Class::get("GLKView").expect("Failed to get class `GLKView`"); - let mut decl = ClassDecl::new("MainGLView", superclass).expect("Failed to declare class `MainGLView`"); + let superclass = + Class::get("GLKView").expect("Failed to get class `GLKView`"); + let mut decl = ClassDecl::new("MainGLView", superclass) + .expect("Failed to declare class `MainGLView`"); unsafe { - decl.add_method(sel!(initWithFrame:), init_with_frame as extern fn(&Object, Sel, CGRect) -> id); - decl.add_class_method(sel!(layerClass), layer_class as extern fn(&Class, Sel) -> *const Class); + decl.add_method( + sel!(initWithFrame:), + init_with_frame as extern "C" fn(&Object, Sel, CGRect) -> id, + ); + decl.add_class_method( + sel!(layerClass), + layer_class as extern "C" fn(&Class, Sel) -> *const Class, + ); decl.register(); } } @@ -390,7 +448,7 @@ impl Drop for Context { } } -impl GlContextExt for Context { +impl ContextTraitExt for Context { type Handle = *mut c_void; #[inline] unsafe fn raw_handle(&self) -> Self::Handle { diff --git a/src/api/mod.rs b/src/api/mod.rs index 25e64666ec..8cca8e8e5f 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,8 +1,8 @@ pub mod android; pub mod caca; -pub mod dlopen; +pub mod dlloader; pub mod egl; pub mod glx; +pub mod ios; pub mod osmesa; pub mod wgl; -pub mod ios; diff --git a/src/api/osmesa/mod.rs b/src/api/osmesa/mod.rs index b95e64e4f4..9c46729544 100644 --- a/src/api/osmesa/mod.rs +++ b/src/api/osmesa/mod.rs @@ -1,7 +1,14 @@ -#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] +#![cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] extern crate osmesa_sys; +use libc; use Api; use ContextError; use CreationError; @@ -11,13 +18,12 @@ use GlRequest; use PixelFormat; use PixelFormatRequirements; use Robustness; -use libc; use std::error::Error; use std::ffi::CString; use std::fmt::{Debug, Display, Error as FormatError, Formatter}; -use std::{mem, ptr}; use std::os::raw::c_void; +use std::{mem, ptr}; pub mod ffi { pub use super::osmesa_sys::OSMesaContext; @@ -35,7 +41,10 @@ struct NoEsOrWebGlSupported; impl Display for NoEsOrWebGlSupported { fn fmt(&self, f: &mut Formatter) -> Result<(), FormatError> { - write!(f, "OsMesa only works with desktop OpenGL; OpenGL ES or WebGL are not supported") + write!( + f, + "OsMesa only works with desktop OpenGL; OpenGL ES or WebGL are not supported" + ) } } @@ -71,19 +80,21 @@ impl OsMesaContext { dimensions: (u32, u32), _pf_reqs: &PixelFormatRequirements, opengl: &GlAttributes<&OsMesaContext>, - ) -> Result - { + ) -> Result { osmesa_sys::OsMesa::try_loading() .map_err(LoadingError::new) .map_err(|e| CreationError::NoBackendAvailable(Box::new(e)))?; - if opengl.sharing.is_some() { panic!("Context sharing not possible with OsMesa") } + if opengl.sharing.is_some() { + panic!("Context sharing not possible with OsMesa") + } match opengl.robustness { - Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + Robustness::RobustNoResetNotification + | Robustness::RobustLoseContextOnReset => { return Err(CreationError::RobustnessNotSupported.into()); - }, - _ => () + } + _ => (), } // TODO: use `pf_reqs` for the format @@ -104,22 +115,28 @@ impl OsMesaContext { } match opengl.version { - GlRequest::Latest => {}, + GlRequest::Latest => {} GlRequest::Specific(Api::OpenGl, (major, minor)) => { attribs.push(osmesa_sys::OSMESA_CONTEXT_MAJOR_VERSION); attribs.push(major as libc::c_int); attribs.push(osmesa_sys::OSMESA_CONTEXT_MINOR_VERSION); attribs.push(minor as libc::c_int); - }, - GlRequest::Specific(Api::OpenGlEs, _) | GlRequest::Specific(Api::WebGl, _) => { - return Err(CreationError::NoBackendAvailable(Box::new(NoEsOrWebGlSupported))); - }, - GlRequest::GlThenGles { opengl_version: (major, minor), .. } => { + } + GlRequest::Specific(Api::OpenGlEs, _) + | GlRequest::Specific(Api::WebGl, _) => { + return Err(CreationError::NoBackendAvailable(Box::new( + NoEsOrWebGlSupported, + ))); + } + GlRequest::GlThenGles { + opengl_version: (major, minor), + .. + } => { attribs.push(osmesa_sys::OSMESA_CONTEXT_MAJOR_VERSION); attribs.push(major as libc::c_int); attribs.push(osmesa_sys::OSMESA_CONTEXT_MINOR_VERSION); attribs.push(minor as libc::c_int); - }, + } } // attribs array must be NULL terminated. @@ -129,14 +146,20 @@ impl OsMesaContext { width: dimensions.0, height: dimensions.1, buffer: ::std::iter::repeat(unsafe { mem::uninitialized() }) - .take((dimensions.0 * dimensions.1) as usize).collect(), + .take((dimensions.0 * dimensions.1) as usize) + .collect(), context: unsafe { - let ctxt = osmesa_sys::OSMesaCreateContextAttribs(attribs.as_ptr(), ptr::null_mut()); - if ctxt.is_null() { - return Err(CreationError::OsError("OSMesaCreateContextAttribs failed".to_string())); + let ctx = osmesa_sys::OSMesaCreateContextAttribs( + attribs.as_ptr(), + ptr::null_mut(), + ); + if ctx.is_null() { + return Err(CreationError::OsError( + "OSMesaCreateContextAttribs failed".to_string(), + )); } - ctxt - } + ctx + }, }) } @@ -152,12 +175,16 @@ impl OsMesaContext { #[inline] pub unsafe fn make_current(&self) -> Result<(), ContextError> { - let ret = osmesa_sys::OSMesaMakeCurrent(self.context, self.buffer.as_ptr() - as *mut _, 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 + let ret = osmesa_sys::OSMesaMakeCurrent( + self.context, + self.buffer.as_ptr() as *mut _, + 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"); } @@ -174,7 +201,9 @@ impl OsMesaContext { pub fn get_proc_address(&self, addr: &str) -> *const () { unsafe { let c_str = CString::new(addr.as_bytes().to_vec()).unwrap(); - mem::transmute(osmesa_sys::OSMesaGetProcAddress(mem::transmute(c_str.as_ptr()))) + mem::transmute(osmesa_sys::OSMesaGetProcAddress(mem::transmute( + c_str.as_ptr(), + ))) } } diff --git a/src/api/wgl/gl.rs b/src/api/wgl/gl.rs index 39b030333d..df656777fe 100644 --- a/src/api/wgl/gl.rs +++ b/src/api/wgl/gl.rs @@ -9,4 +9,4 @@ pub mod wgl_extra { } #[link(name = "opengl32")] -extern {} +extern "C" {} diff --git a/src/api/wgl/make_current_guard.rs b/src/api/wgl/make_current_guard.rs index 54aaeb2003..7a54e67df9 100644 --- a/src/api/wgl/make_current_guard.rs +++ b/src/api/wgl/make_current_guard.rs @@ -1,13 +1,13 @@ +use std::io; use std::marker::PhantomData; use std::os::raw::c_void; -use std::io; use winapi::shared::windef::{HDC, HGLRC}; use CreationError; use super::gl; -/// A guard for when you want to make the context current. Destroying the guard restores the -/// previously-current context. +/// A guard for when you want to make the context current. Destroying the guard +/// restores the previously-current context. pub struct CurrentContextGuard<'a, 'b> { previous_hdc: HDC, previous_hglrc: HGLRC, @@ -16,16 +16,19 @@ pub struct CurrentContextGuard<'a, 'b> { } impl<'a, 'b> CurrentContextGuard<'a, 'b> { - pub unsafe fn make_current(hdc: HDC, context: HGLRC) - -> Result, CreationError> - { + pub unsafe fn make_current( + hdc: HDC, + context: HGLRC, + ) -> Result, CreationError> { let previous_hdc = gl::wgl::GetCurrentDC() as HDC; let previous_hglrc = gl::wgl::GetCurrentContext() as HGLRC; let result = gl::wgl::MakeCurrent(hdc as *const _, context as *const _); if result == 0 { - return Err(CreationError::OsError(format!("wglMakeCurrent function failed: {}", - format!("{}", io::Error::last_os_error())))); + return Err(CreationError::OsError(format!( + "wglMakeCurrent function failed: {}", + format!("{}", io::Error::last_os_error()) + ))); } Ok(CurrentContextGuard { @@ -40,8 +43,10 @@ impl<'a, 'b> CurrentContextGuard<'a, 'b> { impl<'a, 'b> Drop for CurrentContextGuard<'a, 'b> { fn drop(&mut self) { unsafe { - gl::wgl::MakeCurrent(self.previous_hdc as *const c_void, - self.previous_hglrc as *const c_void); + gl::wgl::MakeCurrent( + self.previous_hdc as *const c_void, + self.previous_hglrc as *const c_void, + ); } } } diff --git a/src/api/wgl/mod.rs b/src/api/wgl/mod.rs index 59609bb89d..781e529765 100644 --- a/src/api/wgl/mod.rs +++ b/src/api/wgl/mod.rs @@ -1,33 +1,33 @@ #![cfg(any(target_os = "windows"))] +use Api; use ContextError; use CreationError; use GlAttributes; -use GlRequest; use GlProfile; +use GlRequest; use PixelFormat; use PixelFormatRequirements; use ReleaseBehavior; use Robustness; -use Api; use self::make_current_guard::CurrentContextGuard; use std::ffi::{CStr, CString, OsStr}; -use std::os::raw::{c_void, c_int}; +use std::os::raw::{c_int, c_void}; use std::os::windows::ffi::OsStrExt; use std::{io, mem, ptr}; -use winapi::shared::windef::{HDC, HGLRC, HWND}; use winapi::shared::minwindef::HMODULE; -use winapi::um::wingdi::*; use winapi::shared::minwindef::*; -use winapi::um::winuser::*; -use winapi::um::libloaderapi::*; use winapi::shared::ntdef::LPCWSTR; +use winapi::shared::windef::{HDC, HGLRC, HWND}; +use winapi::um::libloaderapi::*; +use winapi::um::wingdi::*; +use winapi::um::winuser::*; -mod make_current_guard; mod gl; +mod make_current_guard; /// A WGL context. /// @@ -78,14 +78,20 @@ impl Context { /// /// # Unsafety /// - /// The `window` must continue to exist as long as the resulting `Context` exists. - pub unsafe fn new(pf_reqs: &PixelFormatRequirements, opengl: &GlAttributes, - window: HWND) -> Result - { + /// The `window` must continue to exist as long as the resulting `Context` + /// exists. + #[inline] + pub unsafe fn new( + pf_reqs: &PixelFormatRequirements, + opengl: &GlAttributes, + window: HWND, + ) -> Result { let hdc = GetDC(window); if hdc.is_null() { - let err = Err(CreationError::OsError(format!("GetDC function failed: {}", - format!("{}", io::Error::last_os_error())))); + let err = Err(CreationError::OsError(format!( + "GetDC function failed: {}", + format!("{}", io::Error::last_os_error()) + ))); return err; } @@ -97,26 +103,31 @@ impl Context { let data = extra_functions.GetExtensionsStringARB(hdc as *const _); let data = CStr::from_ptr(data).to_bytes().to_vec(); String::from_utf8(data).unwrap() - } else if extra_functions.GetExtensionsStringEXT.is_loaded() { let data = extra_functions.GetExtensionsStringEXT(); let data = CStr::from_ptr(data).to_bytes().to_vec(); String::from_utf8(data).unwrap() - } else { format!("") }; // calling SetPixelFormat let pixel_format = { - let (id, f) = if extensions.split(' ').find(|&i| i == "WGL_ARB_pixel_format") - .is_some() + let (id, f) = if extensions + .split(' ') + .find(|&i| i == "WGL_ARB_pixel_format") + .is_some() { - choose_arb_pixel_format(&extra_functions, &extensions, hdc, pf_reqs) - .map_err(|_| CreationError::NoAvailablePixelFormat)? + choose_arb_pixel_format( + &extra_functions, + &extensions, + hdc, + pf_reqs, + ) + .map_err(|_| CreationError::NoAvailablePixelFormat)? } else { choose_native_pixel_format(hdc, pf_reqs) - .map_err(|_| CreationError::NoAvailablePixelFormat)? + .map_err(|_| CreationError::NoAvailablePixelFormat)? }; set_pixel_format(hdc, id)?; @@ -124,18 +135,29 @@ impl Context { }; // creating the OpenGL context - let context = try!(create_context(Some((&extra_functions, pf_reqs, opengl, &extensions)), - window, hdc)); + let context = try!(create_context( + Some((&extra_functions, pf_reqs, opengl, &extensions)), + window, + hdc + )); // loading the opengl32 module let gl_library = load_opengl32_dll()?; // handling vsync - if extensions.split(' ').find(|&i| i == "WGL_EXT_swap_control").is_some() { + if extensions + .split(' ') + .find(|&i| i == "WGL_EXT_swap_control") + .is_some() + { let _guard = CurrentContextGuard::make_current(hdc, context.0)?; - if extra_functions.SwapIntervalEXT(if opengl.vsync { 1 } else { 0 }) == 0 { - return Err(CreationError::OsError(format!("wglSwapIntervalEXT failed"))); + if extra_functions.SwapIntervalEXT(if opengl.vsync { 1 } else { 0 }) + == 0 + { + return Err(CreationError::OsError(format!( + "wglSwapIntervalEXT failed" + ))); } } @@ -155,7 +177,11 @@ impl Context { #[inline] pub unsafe fn make_current(&self) -> Result<(), ContextError> { - if gl::wgl::MakeCurrent(self.hdc as *const _, self.context.0 as *const _) != 0 { + if gl::wgl::MakeCurrent( + self.hdc as *const _, + self.context.0 as *const _, + ) != 0 + { Ok(()) } else { Err(ContextError::IoError(io::Error::last_os_error())) @@ -164,7 +190,9 @@ impl Context { #[inline] pub fn is_current(&self) -> bool { - unsafe { gl::wgl::GetCurrentContext() == self.context.0 as *const c_void } + unsafe { + gl::wgl::GetCurrentContext() == self.context.0 as *const c_void + } } pub fn get_proc_address(&self, addr: &str) -> *const () { @@ -173,7 +201,9 @@ impl Context { unsafe { let p = gl::wgl::GetProcAddress(addr) as *const (); - if !p.is_null() { return p; } + if !p.is_null() { + return p; + } GetProcAddress(self.gl_library, addr) as *const _ } } @@ -181,11 +211,11 @@ impl Context { #[inline] pub fn swap_buffers(&self) -> Result<(), ContextError> { // TODO: decide how to handle the error - /*if unsafe { SwapBuffers(self.hdc) } != 0 { - Ok(()) - } else { - Err(ContextError::IoError(io::Error::last_os_error())) - }*/ + // if unsafe { SwapBuffers(self.hdc) } != 0 { + // Ok(()) + // } else { + // Err(ContextError::IoError(io::Error::last_os_error())) + // } unsafe { SwapBuffers(self.hdc) }; Ok(()) } @@ -207,70 +237,109 @@ unsafe impl Sync for Context {} /// Creates an OpenGL context. /// -/// If `extra` is `Some`, this function will attempt to use the latest WGL functions to create the -/// context. +/// If `extra` is `Some`, this function will attempt to use the latest WGL +/// functions to create the context. /// -/// Otherwise, only the basic API will be used and the chances of `CreationError::NotSupported` -/// being returned increase. -unsafe fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &PixelFormatRequirements, - &GlAttributes, &str)>, - _: HWND, hdc: HDC) - -> Result -{ +/// Otherwise, only the basic API will be used and the chances of +/// `CreationError::NotSupported` being returned increase. +unsafe fn create_context( + extra: Option<( + &gl::wgl_extra::Wgl, + &PixelFormatRequirements, + &GlAttributes, + &str, + )>, + _: HWND, + hdc: HDC, +) -> Result { let share; if let Some((extra_functions, _pf_reqs, opengl, extensions)) = extra { share = opengl.sharing.unwrap_or(ptr::null_mut()); - if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context").is_some() { + if extensions + .split(' ') + .find(|&i| i == "WGL_ARB_create_context") + .is_some() + { let mut attributes = Vec::new(); match opengl.version { - GlRequest::Latest => {}, + GlRequest::Latest => {} GlRequest::Specific(Api::OpenGl, (major, minor)) => { - attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as c_int); + attributes.push( + gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as c_int, + ); attributes.push(major as c_int); - attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as c_int); + attributes.push( + gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as c_int, + ); attributes.push(minor as c_int); - }, + } GlRequest::Specific(Api::OpenGlEs, (major, minor)) => { - if extensions.split(' ').find(|&i| i == "WGL_EXT_create_context_es2_profile") - .is_some() + if extensions + .split(' ') + .find(|&i| i == "WGL_EXT_create_context_es2_profile") + .is_some() { - attributes.push(gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as c_int); - attributes.push(gl::wgl_extra::CONTEXT_ES2_PROFILE_BIT_EXT as c_int); + attributes.push( + gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as c_int, + ); + attributes.push( + gl::wgl_extra::CONTEXT_ES2_PROFILE_BIT_EXT as c_int, + ); } else { return Err(CreationError::OpenGlVersionNotSupported); } - attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as c_int); + attributes.push( + gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as c_int, + ); attributes.push(major as c_int); - attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as c_int); + attributes.push( + gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as c_int, + ); attributes.push(minor as c_int); - }, - GlRequest::Specific(_, _) => return Err(CreationError::OpenGlVersionNotSupported), - GlRequest::GlThenGles { opengl_version: (major, minor), .. } => { - attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as c_int); + } + GlRequest::Specific(_, _) => { + return Err(CreationError::OpenGlVersionNotSupported); + } + GlRequest::GlThenGles { + opengl_version: (major, minor), + .. + } => { + attributes.push( + gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as c_int, + ); attributes.push(major as c_int); - attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as c_int); + attributes.push( + gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as c_int, + ); attributes.push(minor as c_int); - }, + } } if let Some(profile) = opengl.profile { - if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context_profile").is_some() + if extensions + .split(' ') + .find(|&i| i == "WGL_ARB_create_context_profile") + .is_some() { let flag = match profile { - GlProfile::Compatibility => - gl::wgl_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, - GlProfile::Core => - gl::wgl_extra::CONTEXT_CORE_PROFILE_BIT_ARB, + GlProfile::Compatibility => { + gl::wgl_extra::CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB + } + GlProfile::Core => { + gl::wgl_extra::CONTEXT_CORE_PROFILE_BIT_ARB + } }; - attributes.push(gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as c_int); + attributes + .push(gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as c_int); attributes.push(flag as c_int); } else { return Err(CreationError::NotSupported( - "required extension \"WGL_ARB_create_context_profile\" not found")); + "required extension \"WGL_ARB_create_context_profile\" not found", + )); } } @@ -278,32 +347,54 @@ unsafe fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &PixelFormatRequire let mut flags = 0; // robustness - if extensions.split(' ').find(|&i| i == "WGL_ARB_create_context_robustness").is_some() { + if extensions + .split(' ') + .find(|&i| i == "WGL_ARB_create_context_robustness") + .is_some() + { match opengl.robustness { - Robustness::RobustNoResetNotification | Robustness::TryRobustNoResetNotification => { - attributes.push(gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int); - attributes.push(gl::wgl_extra::NO_RESET_NOTIFICATION_ARB as c_int); - flags = flags | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int; - }, - Robustness::RobustLoseContextOnReset | Robustness::TryRobustLoseContextOnReset => { - attributes.push(gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int); - attributes.push(gl::wgl_extra::LOSE_CONTEXT_ON_RESET_ARB as c_int); - flags = flags | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB as c_int; - }, + Robustness::RobustNoResetNotification + | Robustness::TryRobustNoResetNotification => { + attributes.push( + gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int, + ); + attributes.push( + gl::wgl_extra::NO_RESET_NOTIFICATION_ARB + as c_int, + ); + flags = flags + | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB + as c_int; + } + Robustness::RobustLoseContextOnReset + | Robustness::TryRobustLoseContextOnReset => { + attributes.push( + gl::wgl_extra::CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB as c_int, + ); + attributes.push( + gl::wgl_extra::LOSE_CONTEXT_ON_RESET_ARB + as c_int, + ); + flags = flags + | gl::wgl_extra::CONTEXT_ROBUST_ACCESS_BIT_ARB + as c_int; + } Robustness::NotRobust => (), Robustness::NoError => (), } } else { match opengl.robustness { - Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + Robustness::RobustNoResetNotification + | Robustness::RobustLoseContextOnReset => { return Err(CreationError::RobustnessNotSupported); - }, - _ => () + } + _ => (), } } if opengl.debug { - flags = flags | gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as c_int; + flags = + flags | gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as c_int; } flags @@ -314,44 +405,52 @@ unsafe fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &PixelFormatRequire attributes.push(0); - let ctxt = extra_functions.CreateContextAttribsARB(hdc as *const c_void, - share as *const c_void, - attributes.as_ptr()); - - if ctxt.is_null() { - return Err(CreationError::OsError(format!("wglCreateContextAttribsARB failed: {}", - format!("{}", io::Error::last_os_error())))); + let ctx = extra_functions.CreateContextAttribsARB( + hdc as *const c_void, + share as *const c_void, + attributes.as_ptr(), + ); + + if ctx.is_null() { + return Err(CreationError::OsError(format!( + "wglCreateContextAttribsARB failed: {}", + format!("{}", io::Error::last_os_error()) + ))); } else { - return Ok(ContextWrapper(ctxt as HGLRC)); + return Ok(ContextWrapper(ctx as HGLRC)); } } - } else { share = ptr::null_mut(); } - let ctxt = gl::wgl::CreateContext(hdc as *const c_void); - if ctxt.is_null() { - return Err(CreationError::OsError(format!("wglCreateContext failed: {}", - format!("{}", io::Error::last_os_error())))); + let ctx = gl::wgl::CreateContext(hdc as *const c_void); + if ctx.is_null() { + return Err(CreationError::OsError(format!( + "wglCreateContext failed: {}", + format!("{}", io::Error::last_os_error()) + ))); } if !share.is_null() { - if gl::wgl::ShareLists(share as *const c_void, ctxt) == 0 { - return Err(CreationError::OsError(format!("wglShareLists failed: {}", - format!("{}", io::Error::last_os_error())))); + if gl::wgl::ShareLists(share as *const c_void, ctx) == 0 { + return Err(CreationError::OsError(format!( + "wglShareLists failed: {}", + format!("{}", io::Error::last_os_error()) + ))); } }; - Ok(ContextWrapper(ctxt as HGLRC)) + Ok(ContextWrapper(ctx as HGLRC)) } /// Chooses a pixel formats without using WGL. /// /// Gives less precise results than `enumerate_arb_pixel_formats`. -unsafe fn choose_native_pixel_format(hdc: HDC, reqs: &PixelFormatRequirements) - -> Result<(c_int, PixelFormat), ()> -{ +unsafe fn choose_native_pixel_format( + hdc: HDC, + reqs: &PixelFormatRequirements, +) -> Result<(c_int, PixelFormat), ()> { // TODO: hardware acceleration is not handled // handling non-supported stuff @@ -362,7 +461,7 @@ unsafe fn choose_native_pixel_format(hdc: HDC, reqs: &PixelFormatRequirements) match reqs.multisampling { Some(0) => (), None => (), - Some(_) => return Err(()) + Some(_) => return Err(()), }; if reqs.stereoscopy { @@ -383,16 +482,12 @@ unsafe fn choose_native_pixel_format(hdc: HDC, reqs: &PixelFormatRequirements) nVersion: 1, dwFlags: { let f1 = match reqs.double_buffer { - None => PFD_DOUBLEBUFFER, // Should be PFD_DOUBLEBUFFER_DONTCARE after you can choose + None => PFD_DOUBLEBUFFER, /* Should be PFD_DOUBLEBUFFER_DONTCARE after you can choose */ Some(true) => PFD_DOUBLEBUFFER, Some(false) => 0, }; - let f2 = if reqs.stereoscopy { - PFD_STEREO - } else { - 0 - }; + let f2 = if reqs.stereoscopy { PFD_STEREO } else { 0 }; PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | f1 | f2 }, @@ -429,14 +524,18 @@ unsafe fn choose_native_pixel_format(hdc: HDC, reqs: &PixelFormatRequirements) // querying back the capabilities of what windows told us let mut output: PIXELFORMATDESCRIPTOR = mem::zeroed(); - if DescribePixelFormat(hdc, pf_id, mem::size_of::() as u32, - &mut output) == 0 + if DescribePixelFormat( + hdc, + pf_id, + mem::size_of::() as u32, + &mut output, + ) == 0 { return Err(()); } - // windows may return us a non-conforming pixel format if none are supported, so we have to - // check this + // windows may return us a non-conforming pixel format if none are + // supported, so we have to check this if (output.dwFlags & PFD_DRAW_TO_WINDOW) == 0 { return Err(()); } @@ -488,10 +587,12 @@ unsafe fn choose_native_pixel_format(hdc: HDC, reqs: &PixelFormatRequirements) /// Enumerates the list of pixel formats by using extra WGL functions. /// /// Gives more precise results than `enumerate_native_pixel_formats`. -unsafe fn choose_arb_pixel_format(extra: &gl::wgl_extra::Wgl, extensions: &str, - hdc: HDC, reqs: &PixelFormatRequirements) - -> Result<(c_int, PixelFormat), ()> -{ +unsafe fn choose_arb_pixel_format( + extra: &gl::wgl_extra::Wgl, + extensions: &str, + hdc: HDC, + reqs: &PixelFormatRequirements, +) -> Result<(c_int, PixelFormat), ()> { let descriptor = { let mut out: Vec = Vec::with_capacity(37); @@ -503,7 +604,11 @@ unsafe fn choose_arb_pixel_format(extra: &gl::wgl_extra::Wgl, extensions: &str, out.push(gl::wgl_extra::PIXEL_TYPE_ARB as c_int); if reqs.float_color_buffer { - if extensions.split(' ').find(|&i| i == "WGL_ARB_pixel_format_float").is_some() { + if extensions + .split(' ') + .find(|&i| i == "WGL_ARB_pixel_format_float") + .is_some() + { out.push(gl::wgl_extra::TYPE_RGBA_FLOAT_ARB as c_int); } else { return Err(()); @@ -541,13 +646,18 @@ unsafe fn choose_arb_pixel_format(extra: &gl::wgl_extra::Wgl, extensions: &str, out.push(stencil as c_int); } - // Prefer double buffering if unspecified (probably shouldn't once you can choose) + // Prefer double buffering if unspecified (probably shouldn't once you + // can choose) let double_buffer = reqs.double_buffer.unwrap_or(true); out.push(gl::wgl_extra::DOUBLE_BUFFER_ARB as c_int); out.push(if double_buffer { 1 } else { 0 }); if let Some(multisampling) = reqs.multisampling { - if extensions.split(' ').find(|&i| i == "WGL_ARB_multisample").is_some() { + if extensions + .split(' ') + .find(|&i| i == "WGL_ARB_multisample") + .is_some() + { out.push(gl::wgl_extra::SAMPLE_BUFFERS_ARB as c_int); out.push(if multisampling == 0 { 0 } else { 1 }); out.push(gl::wgl_extra::SAMPLES_ARB as c_int); @@ -561,10 +671,18 @@ unsafe fn choose_arb_pixel_format(extra: &gl::wgl_extra::Wgl, extensions: &str, out.push(if reqs.stereoscopy { 1 } else { 0 }); if reqs.srgb { - if extensions.split(' ').find(|&i| i == "WGL_ARB_framebuffer_sRGB").is_some() { + if extensions + .split(' ') + .find(|&i| i == "WGL_ARB_framebuffer_sRGB") + .is_some() + { out.push(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as c_int); out.push(1); - } else if extensions.split(' ').find(|&i| i == "WGL_EXT_framebuffer_sRGB").is_some() { + } else if extensions + .split(' ') + .find(|&i| i == "WGL_EXT_framebuffer_sRGB") + .is_some() + { out.push(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT as c_int); out.push(1); } else { @@ -575,11 +693,20 @@ unsafe fn choose_arb_pixel_format(extra: &gl::wgl_extra::Wgl, extensions: &str, match reqs.release_behavior { ReleaseBehavior::Flush => (), ReleaseBehavior::None => { - if extensions.split(' ').find(|&i| i == "WGL_ARB_context_flush_control").is_some() { - out.push(gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int); - out.push(gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB as c_int); + if extensions + .split(' ') + .find(|&i| i == "WGL_ARB_context_flush_control") + .is_some() + { + out.push( + gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_ARB as c_int, + ); + out.push( + gl::wgl_extra::CONTEXT_RELEASE_BEHAVIOR_NONE_ARB + as c_int, + ); } - }, + } } out.push(0); @@ -588,8 +715,14 @@ unsafe fn choose_arb_pixel_format(extra: &gl::wgl_extra::Wgl, extensions: &str, let mut format_id = mem::uninitialized(); let mut num_formats = mem::uninitialized(); - if extra.ChoosePixelFormatARB(hdc as *const _, descriptor.as_ptr(), ptr::null(), 1, - &mut format_id, &mut num_formats) == 0 + if extra.ChoosePixelFormatARB( + hdc as *const _, + descriptor.as_ptr(), + ptr::null(), + 1, + &mut format_id, + &mut num_formats, + ) == 0 { return Err(()); } @@ -600,25 +733,34 @@ unsafe fn choose_arb_pixel_format(extra: &gl::wgl_extra::Wgl, extensions: &str, let get_info = |attrib: u32| { let mut value = mem::uninitialized(); - extra.GetPixelFormatAttribivARB(hdc as *const _, format_id as c_int, - 0, 1, [attrib as c_int].as_ptr(), - &mut value); + extra.GetPixelFormatAttribivARB( + hdc as *const _, + format_id as c_int, + 0, + 1, + [attrib as c_int].as_ptr(), + &mut value, + ); value as u32 }; let pf_desc = PixelFormat { - hardware_accelerated: get_info(gl::wgl_extra::ACCELERATION_ARB) != - gl::wgl_extra::NO_ACCELERATION_ARB, - color_bits: get_info(gl::wgl_extra::RED_BITS_ARB) as u8 + - get_info(gl::wgl_extra::GREEN_BITS_ARB) as u8 + - get_info(gl::wgl_extra::BLUE_BITS_ARB) as u8, + hardware_accelerated: get_info(gl::wgl_extra::ACCELERATION_ARB) + != gl::wgl_extra::NO_ACCELERATION_ARB, + color_bits: get_info(gl::wgl_extra::RED_BITS_ARB) as u8 + + get_info(gl::wgl_extra::GREEN_BITS_ARB) as u8 + + get_info(gl::wgl_extra::BLUE_BITS_ARB) as u8, alpha_bits: get_info(gl::wgl_extra::ALPHA_BITS_ARB) as u8, depth_bits: get_info(gl::wgl_extra::DEPTH_BITS_ARB) as u8, stencil_bits: get_info(gl::wgl_extra::STENCIL_BITS_ARB) as u8, stereoscopy: get_info(gl::wgl_extra::STEREO_ARB) != 0, double_buffer: get_info(gl::wgl_extra::DOUBLE_BUFFER_ARB) != 0, multisampling: { - if extensions.split(' ').find(|&i| i == "WGL_ARB_multisample").is_some() { + if extensions + .split(' ') + .find(|&i| i == "WGL_ARB_multisample") + .is_some() + { match get_info(gl::wgl_extra::SAMPLES_ARB) { 0 => None, a => Some(a as u16), @@ -627,9 +769,17 @@ unsafe fn choose_arb_pixel_format(extra: &gl::wgl_extra::Wgl, extensions: &str, None } }, - srgb: if extensions.split(' ').find(|&i| i == "WGL_ARB_framebuffer_sRGB").is_some() { + srgb: if extensions + .split(' ') + .find(|&i| i == "WGL_ARB_framebuffer_sRGB") + .is_some() + { get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB) != 0 - } else if extensions.split(' ').find(|&i| i == "WGL_EXT_framebuffer_sRGB").is_some() { + } else if extensions + .split(' ') + .find(|&i| i == "WGL_EXT_framebuffer_sRGB") + .is_some() + { get_info(gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT) != 0 } else { false @@ -643,16 +793,24 @@ unsafe fn choose_arb_pixel_format(extra: &gl::wgl_extra::Wgl, extensions: &str, unsafe fn set_pixel_format(hdc: HDC, id: c_int) -> Result<(), CreationError> { let mut output: PIXELFORMATDESCRIPTOR = mem::zeroed(); - if DescribePixelFormat(hdc, id, mem::size_of::() - as UINT, &mut output) == 0 + if DescribePixelFormat( + hdc, + id, + mem::size_of::() as UINT, + &mut output, + ) == 0 { - return Err(CreationError::OsError(format!("DescribePixelFormat function failed: {}", - format!("{}", io::Error::last_os_error())))); + return Err(CreationError::OsError(format!( + "DescribePixelFormat function failed: {}", + format!("{}", io::Error::last_os_error()) + ))); } if SetPixelFormat(hdc, id, &output) == 0 { - return Err(CreationError::OsError(format!("SetPixelFormat function failed: {}", - format!("{}", io::Error::last_os_error())))); + return Err(CreationError::OsError(format!( + "SetPixelFormat function failed: {}", + format!("{}", io::Error::last_os_error()) + ))); } Ok(()) @@ -660,14 +818,18 @@ unsafe fn set_pixel_format(hdc: HDC, id: c_int) -> Result<(), CreationError> { /// Loads the `opengl32.dll` library. unsafe fn load_opengl32_dll() -> Result { - let name = OsStr::new("opengl32.dll").encode_wide().chain(Some(0).into_iter()) - .collect::>(); + let name = OsStr::new("opengl32.dll") + .encode_wide() + .chain(Some(0).into_iter()) + .collect::>(); let lib = LoadLibraryW(name.as_ptr()); if lib.is_null() { - return Err(CreationError::OsError(format!("LoadLibrary function failed: {}", - format!("{}", io::Error::last_os_error())))); + return Err(CreationError::OsError(format!( + "LoadLibrary function failed: {}", + format!("{}", io::Error::last_os_error()) + ))); } Ok(lib) @@ -675,11 +837,15 @@ unsafe fn load_opengl32_dll() -> Result { /// Loads the WGL functions that are not guaranteed to be supported. /// -/// The `window` must be passed because the driver can vary depending on the window's -/// characteristics. -unsafe fn load_extra_functions(window: HWND) -> Result { - let (ex_style, style) = (WS_EX_APPWINDOW, WS_POPUP | - WS_CLIPSIBLINGS | WS_CLIPCHILDREN); +/// The `window` must be passed because the driver can vary depending on the +/// window's characteristics. +unsafe fn load_extra_functions( + window: HWND, +) -> Result { + let (ex_style, style) = ( + WS_EX_APPWINDOW, + WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + ); // creating a dummy invisible window let dummy_window = { @@ -696,8 +862,10 @@ unsafe fn load_extra_functions(window: HWND) -> Result Result>(); + let class_name = OsStr::new("WglDummy Class") + .encode_wide() + .chain(Some(0).into_iter()) + .collect::>(); class.cbSize = mem::size_of::() as UINT; class.lpszClassName = class_name.as_ptr(); class.lpfnWndProc = Some(DefWindowProcW); - // this shouldn't fail if the registration of the real window class worked. - // multiple registrations of the window class trigger an error which we want - // to ignore silently (e.g for multi-window setups) + // this shouldn't fail if the registration of the real window class + // worked. multiple registrations of the window class trigger an + // error which we want to ignore silently (e.g for multi-window + // setups) RegisterClassExW(&class); - // this dummy window should match the real one enough to get the same OpenGL driver - let title = OsStr::new("dummy window").encode_wide().chain(Some(0).into_iter()) - .collect::>(); - let win = CreateWindowExW(ex_style, class_name.as_ptr(), - title.as_ptr() as LPCWSTR, style, - CW_USEDEFAULT, CW_USEDEFAULT, - rect.right - rect.left, - rect.bottom - rect.top, - ptr::null_mut(), ptr::null_mut(), - GetModuleHandleW(ptr::null()), - ptr::null_mut()); + // this dummy window should match the real one enough to get the same + // OpenGL driver + let title = OsStr::new("dummy window") + .encode_wide() + .chain(Some(0).into_iter()) + .collect::>(); + let win = CreateWindowExW( + ex_style, + class_name.as_ptr(), + title.as_ptr() as LPCWSTR, + style, + CW_USEDEFAULT, + CW_USEDEFAULT, + rect.right - rect.left, + rect.bottom - rect.top, + ptr::null_mut(), + ptr::null_mut(), + GetModuleHandleW(ptr::null()), + ptr::null_mut(), + ); if win.is_null() { - return Err(CreationError::OsError(format!("CreateWindowEx function failed: {}", - format!("{}", io::Error::last_os_error())))); + return Err(CreationError::OsError(format!( + "CreateWindowEx function failed: {}", + format!("{}", io::Error::last_os_error()) + ))); } let hdc = GetDC(win); if hdc.is_null() { - let err = Err(CreationError::OsError(format!("GetDC function failed: {}", - format!("{}", io::Error::last_os_error())))); + let err = Err(CreationError::OsError(format!( + "GetDC function failed: {}", + format!("{}", io::Error::last_os_error()) + ))); return err; } @@ -758,8 +944,8 @@ unsafe fn load_extra_functions(window: HWND) -> Result Result { // now querying let pf_id = unsafe { ChoosePixelFormat(hdc, &descriptor) }; if pf_id == 0 { - return Err(CreationError::OsError("No available pixel format".to_owned())); + return Err(CreationError::OsError( + "No available pixel format".to_owned(), + )); } Ok(pf_id) diff --git a/src/combined.rs b/src/combined.rs new file mode 100644 index 0000000000..b0b646eaf6 --- /dev/null +++ b/src/combined.rs @@ -0,0 +1,131 @@ +use super::*; + +/// Represents an OpenGL context and the `Window` with which it is associated. +/// +/// # Example +/// +/// ```no_run +/// # extern crate glutin; +/// # use glutin::ContextTrait; +/// # fn main() { +/// let mut el = glutin::EventsLoop::new(); +/// let wb = glutin::WindowBuilder::new(); +/// let combined_context = glutin::ContextBuilder::new() +/// .build_combined(wb, &el) +/// .unwrap(); +/// +/// unsafe { combined_context.make_current().unwrap() }; +/// +/// loop { +/// el.poll_events(|event| { +/// match event { +/// // process events here +/// _ => (), +/// } +/// }); +/// +/// // draw everything here +/// +/// combined_context.swap_buffers(); +/// std::thread::sleep(std::time::Duration::from_millis(17)); +/// } +/// # } +/// ``` +pub struct CombinedContext { + context: Context, + window: Window, +} + +impl CombinedContext { + /// Builds the given window along with the associated GL context, returning + /// the pair as a `CombinedContext`. + /// + /// One notable limitation of the Wayland backend when it comes to shared + /// contexts is that both contexts must use the same events loop. + /// + /// Errors can occur in two scenarios: + /// - If the window could not be created (via permission denied, + /// incompatible system, out of memory, etc.). This should be very rare. + /// - If the OpenGL context could not be created. This generally happens + /// because the underlying platform doesn't support a requested feature. + pub fn new( + wb: WindowBuilder, + cb: ContextBuilder, + el: &EventsLoop, + ) -> Result { + let ContextBuilder { pf_reqs, gl_attr } = cb; + let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); + platform::Context::new(wb, el, &pf_reqs, &gl_attr).map( + |(window, context)| CombinedContext { + window, + context: Context { context }, + }, + ) + } + + /// Borrow the inner `Window`. + pub fn window(&self) -> &Window { + &self.window + } + + /// Borrow the inner GL `Context`. + pub fn context(&self) -> &Context { + &self.context + } + + /// 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. + /// + /// **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. + pub fn swap_buffers(&self) -> Result<(), ContextError> { + self.context.context.swap_buffers() + } + + /// Returns the pixel format of the main framebuffer of the context. + pub fn get_pixel_format(&self) -> PixelFormat { + self.context.context.get_pixel_format() + } + + /// Resize the context. + /// + /// Some platforms (macOS, Wayland) require being manually updated when + /// their window or surface is resized. + /// + /// The easiest way of doing this is to take every `Resized` window event + /// that is received with a `LogicalSize` and convert it to a + /// `PhysicalSize` and pass it into this function. + pub fn resize(&self, size: dpi::PhysicalSize) { + let (width, height) = size.into(); + self.context.context.resize(width, height); + } +} + +impl ContextTrait for CombinedContext { + unsafe fn make_current(&self) -> Result<(), ContextError> { + self.context.make_current() + } + + fn is_current(&self) -> bool { + self.context.is_current() + } + + fn get_proc_address(&self, addr: &str) -> *const () { + self.context.get_proc_address(addr) + } + + fn get_api(&self) -> Api { + self.context.get_api() + } +} + +impl std::ops::Deref for CombinedContext { + type Target = Window; + fn deref(&self) -> &Self::Target { + &self.window + } +} diff --git a/src/context.rs b/src/context.rs new file mode 100644 index 0000000000..28f772c24c --- /dev/null +++ b/src/context.rs @@ -0,0 +1,67 @@ +use super::*; + +/// Represents an OpenGL context. +/// +/// A `Context` is normally associated with a single Window, however `Context`s +/// can be *shared* between multiple windows or be headless. +/// +/// # Example +/// +/// ```no_run +/// # extern crate glutin; +/// # use glutin::ContextTrait; +/// # fn main() { +/// # let el = glutin::EventsLoop::new(); +/// # let wb = glutin::WindowBuilder::new(); +/// # let some_context = glutin::ContextBuilder::new() +/// # .build_combined(wb, &el) +/// # .unwrap(); +/// let cb = glutin::ContextBuilder::new() +/// .with_vsync(true) +/// .with_multisampling(8) +/// .with_shared_lists(some_context.context()); +/// # } +/// ``` +pub struct Context { + pub(crate) context: platform::Context, +} + +impl ContextTrait for Context { + unsafe fn make_current(&self) -> Result<(), ContextError> { + self.context.make_current() + } + + fn is_current(&self) -> bool { + self.context.is_current() + } + + fn get_proc_address(&self, addr: &str) -> *const () { + self.context.get_proc_address(addr) + } + + fn get_api(&self) -> Api { + self.context.get_api() + } +} + +impl Context { + /// Builds the given GL context. + /// + /// One notable limitation of the Wayland backend when it comes to shared + /// contexts is that both contexts must use the same events loop. + /// + /// Errors can occur in two scenarios: + /// - If the window could not be created (via permission denied, + /// incompatible system, out of memory, etc.). This should be very rare. + /// - If the OpenGL context could not be created. This generally happens + /// because the underlying platform doesn't support a requested feature. + pub fn new( + el: &winit::EventsLoop, + cb: ContextBuilder, + ) -> Result { + let ContextBuilder { pf_reqs, gl_attr } = cb; + let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); + platform::Context::new_context(el, &pf_reqs, &gl_attr) + .map(|context| Context { context }) + } +} diff --git a/src/lib.rs b/src/lib.rs index da99011881..1968a2eccb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,43 +1,42 @@ -//! The purpose of this library is to provide an OpenGL context on as many platforms as possible. +//! The purpose of this library is to provide an OpenGL context on as many +//! platforms as possible. //! -//! # Building a GlWindow +//! # Building a CombinedContext //! -//! A `GlWindow` is composed of a `Window` and an OpenGL `Context`. Due to some -//! operating-system-specific quirks, glutin requires control over the order of creation of the -//! `Context` and `Window`. Here is an example of building a GlWindow: +//! A `CombinedContext` is composed of a `Window` and an OpenGL `Context`. +//! +//! Due to some operating-system-specific quirks, glutin prefers control over +//! the order of creation of the `Context` and `Window`. Here is an example of +//! building a CombinedContext the prefered way: //! //! ```no_run //! # extern crate glutin; //! # fn main() { -//! let events_loop = glutin::EventsLoop::new(); -//! let window = glutin::WindowBuilder::new() +//! let el = glutin::EventsLoop::new(); +//! let wb = glutin::WindowBuilder::new() //! .with_title("Hello world!") //! .with_dimensions(glutin::dpi::LogicalSize::new(1024.0, 768.0)); -//! let context = glutin::ContextBuilder::new(); -//! let gl_window = glutin::GlWindow::new(window, context, &events_loop).unwrap(); +//! let combined_context = glutin::ContextBuilder::new() +//! .build_combined(wb, &el) +//! .unwrap(); //! # } //! ``` //! -//! # Features -//! -//! This crate has one Cargo feature: `window`. -//! -//! - `window` allows you to create regular windows and enables the `WindowBuilder` object. -//! -//! By default `window` is enabled. +//! You can, of course, create an OpenGL `Context` separately from an existing +//! window, however that may result in an suboptimal configuration of the window +//! on some platforms. In that case use "SeparatedContext". -#[cfg(target_os = "windows")] #[macro_use] extern crate lazy_static; - -#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] +#[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] #[macro_use] extern crate shared_library; - -extern crate libc; - -extern crate winit; - #[cfg(target_os = "windows")] extern crate winapi; #[cfg(any(target_os = "macos", target_os = "ios"))] @@ -51,52 +50,58 @@ extern crate cocoa; extern crate core_foundation; #[cfg(target_os = "macos")] extern crate core_graphics; -#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))] -extern crate x11_dl; -#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd"))] +extern crate libc; +#[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] +extern crate libloading; +#[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "netbsd", + target_os = "openbsd" +))] extern crate wayland_client; - -pub use winit::{ - AvailableMonitorsIter, - AxisId, - ButtonId, - ControlFlow, - CreationError as WindowCreationError, - DeviceEvent, - DeviceId, - dpi, - ElementState, - Event, - EventsLoop, - EventsLoopClosed, - EventsLoopProxy, - Icon, - KeyboardInput, - ModifiersState, - MonitorId, - MouseButton, - MouseCursor, - MouseScrollDelta, - ScanCode, - Touch, - TouchPhase, - VirtualKeyCode, - Window, - WindowAttributes, - WindowBuilder, - WindowEvent, - WindowId, -}; - -use std::io; +extern crate winit; +#[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "dragonfly", + target_os = "netbsd", + target_os = "openbsd" +))] +extern crate x11_dl; mod api; +mod combined; +mod context; mod platform; +mod separated; pub mod os; +pub use combined::CombinedContext; +pub use context::Context; +pub use separated::SeparatedContext; + +use std::io; +pub use winit::{ + dpi, AvailableMonitorsIter, AxisId, ButtonId, ControlFlow, + CreationError as WindowCreationError, DeviceEvent, DeviceId, ElementState, + Event, EventsLoop, EventsLoopClosed, EventsLoopProxy, Icon, KeyboardInput, + ModifiersState, MonitorId, MouseButton, MouseCursor, MouseScrollDelta, + ScanCode, Touch, TouchPhase, VirtualKeyCode, Window, WindowAttributes, + WindowBuilder, WindowEvent, WindowId, +}; + /// A trait for types associated with a GL context. -pub trait GlContext +pub trait ContextTrait where Self: Sized, { @@ -113,31 +118,6 @@ where fn get_api(&self) -> Api; } -/// Represents an OpenGL context. -/// -/// A `Context` is normally associated with a single Window, however `Context`s can be *shared* -/// between multiple windows. -/// -/// # Example -/// -/// ```no_run -/// # extern crate glutin; -/// # use glutin::GlContext; -/// # fn main() { -/// # let events_loop = glutin::EventsLoop::new(); -/// # let window = glutin::WindowBuilder::new(); -/// # let context = glutin::ContextBuilder::new(); -/// # let some_gl_window = glutin::GlWindow::new(window, context, &events_loop).unwrap(); -/// let context = glutin::ContextBuilder::new() -/// .with_vsync(true) -/// .with_multisampling(8) -/// .with_shared_lists(some_gl_window.context()); -/// # } -/// ``` -pub struct Context { - context: platform::Context, -} - /// Object that allows you to build `Context`s. pub struct ContextBuilder<'a> { /// The attributes to use to create the context. @@ -146,41 +126,6 @@ pub struct ContextBuilder<'a> { pf_reqs: PixelFormatRequirements, } -/// Represents an OpenGL context and a Window with which it is associated. -/// -/// # Example -/// -/// ```no_run -/// # extern crate glutin; -/// # use glutin::GlContext; -/// # fn main() { -/// let mut events_loop = glutin::EventsLoop::new(); -/// let window = glutin::WindowBuilder::new(); -/// let context = glutin::ContextBuilder::new(); -/// let gl_window = glutin::GlWindow::new(window, context, &events_loop).unwrap(); -/// -/// unsafe { gl_window.make_current().unwrap() }; -/// -/// loop { -/// events_loop.poll_events(|event| { -/// match event { -/// // process events here -/// _ => () -/// } -/// }); -/// -/// // draw everything here -/// -/// gl_window.swap_buffers(); -/// std::thread::sleep(std::time::Duration::from_millis(17)); -/// } -/// # } -/// ``` -pub struct GlWindow { - context: Context, - window: Window, -} - impl<'a> ContextBuilder<'a> { /// Initializes a new `ContextBuilder` with default values. pub fn new() -> Self { @@ -206,8 +151,9 @@ impl<'a> ContextBuilder<'a> { /// Sets the *debug* flag for the OpenGL context. /// - /// The default value for this flag is `cfg!(debug_assertions)`, which means that it's enabled - /// when you run `cargo build` and disabled when you run `cargo build --release`. + /// The default value for this flag is `cfg!(debug_assertions)`, which means + /// that it's enabled when you run `cargo build` and disabled when you run + /// `cargo build --release`. #[inline] pub fn with_gl_debug_flag(mut self, flag: bool) -> Self { self.gl_attr.debug = flag; @@ -237,8 +183,8 @@ impl<'a> ContextBuilder<'a> { self } - /// Sets the multisampling level to request. A value of `0` indicates that multisampling must - /// not be enabled. + /// Sets the multisampling level to request. A value of `0` indicates that + /// multisampling must not be enabled. /// /// # Panic /// @@ -304,7 +250,6 @@ impl<'a> ContextBuilder<'a> { /// * MacOS /// * Linux using GLX with X /// * Windows using WGL - /// #[inline] pub fn with_double_buffer(mut self, double_buffer: Option) -> Self { self.pf_reqs.double_buffer = double_buffer; @@ -323,164 +268,39 @@ impl<'a> ContextBuilder<'a> { /// * Linux using EGL with either X or Wayland /// * Windows using EGL or WGL /// * Android using EGL - /// #[inline] - pub fn with_hardware_acceleration(mut self, acceleration: Option) -> Self { + pub fn with_hardware_acceleration( + mut self, + acceleration: Option, + ) -> Self { self.pf_reqs.hardware_accelerated = acceleration; self } - /// Builds the context as a headless context. - pub fn build(self, el: &EventsLoop, shareable_with_windowed_contexts: bool) -> Result { - Context::new(el, self, shareable_with_windowed_contexts) - } -} - -impl GlWindow { - /// Builds the given window along with the associated GL context, returning the pair as a - /// `GlWindow`. - /// - /// Errors can occur in two scenarios: - /// - If the window could not be created (via permission denied, incompatible system, out of - /// memory, etc.). This should be very rare. - /// - If the OpenGL context could not be created. This generally happens because the underlying - /// platform doesn't support a requested feature. - pub fn new( - window_builder: WindowBuilder, - context_builder: ContextBuilder, - events_loop: &EventsLoop, - ) -> Result - { - let ContextBuilder { pf_reqs, gl_attr } = context_builder; - let gl_attr = gl_attr.map_sharing(|ctxt| &ctxt.context); - - // Not all platforms support context sharing yet, when they do, their - // `new.*` functions should be marked unsafe. - #[allow(unused_unsafe)] - unsafe { - platform::Context::new(window_builder, events_loop, &pf_reqs, &gl_attr) - .map(|(window, context)| GlWindow { - window, - context: Context { context }, - }) - } + /// Builds a headless context. + pub fn build_headless( + self, + el: &EventsLoop, + ) -> Result { + Context::new(el, self) } - /// Borrow the inner `Window`. - pub fn window(&self) -> &Window { - &self.window + /// Builds a context and it's associated window. + pub fn build_combined( + self, + wb: WindowBuilder, + el: &EventsLoop, + ) -> Result { + CombinedContext::new(wb, self, el) } - /// Borrow the inner GL `Context`. - pub fn context(&self) -> &Context { - &self.context - } - - /// 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. - /// - /// **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. - pub fn swap_buffers(&self) -> Result<(), ContextError> { - self.context.context.swap_buffers() - } - - /// Returns the pixel format of the main framebuffer of the context. - pub fn get_pixel_format(&self) -> PixelFormat { - self.context.context.get_pixel_format() - } - - /// Resize the context. - /// - /// Some platforms (macOS, Wayland) require being manually updated when their window or - /// surface is resized. - /// - /// The easiest way of doing this is to take every `Resized` window event that - /// is received with a `LogicalSize` and convert it to a `PhysicalSize` and - /// pass it into this function. - pub fn resize(&self, size: dpi::PhysicalSize) { - let (width, height) = size.into(); - self.context.context.resize(width, height); - } -} - -impl GlContext for Context { - unsafe fn make_current(&self) -> Result<(), ContextError> { - self.context.make_current() - } - - fn is_current(&self) -> bool { - self.context.is_current() - } - - fn get_proc_address(&self, addr: &str) -> *const () { - self.context.get_proc_address(addr) - } - - fn get_api(&self) -> Api { - self.context.get_api() - } -} - -impl Context { - /// Builds the given GL context. - /// - /// Contexts made with the `shareable_with_windowed_contexts` flag set to - /// `true` can be shared with: - /// - contexts made with that flag set to `true`; and - /// - contexts made when creating a `GlWindow`. - /// - /// If the flag is set to `false` on the other hand, the context should only - /// be shared with other contexts made with the flag set to `false`. - /// - /// Some platforms only implement contexts that are shareable with windowed contexts. On those - /// platforms, the `shareable_with_windowed_contexts` flag will be ignored and always behave as - /// if it were set to `true`. - /// - /// Errors can occur if the OpenGL context could not be created. This generally happens because - /// the underlying platform doesn't support a requested feature, or if one of the sharing - /// restrictions above wasn't abided by. - pub fn new( - el: &winit::EventsLoop, - context_builder: ContextBuilder, - shareable_with_windowed_contexts: bool, - ) -> Result - { - let ContextBuilder { pf_reqs, gl_attr } = context_builder; - let gl_attr = gl_attr.map_sharing(|ctxt| &ctxt.context); - - unsafe { - platform::Context::new_context(el, &pf_reqs, &gl_attr, shareable_with_windowed_contexts) - .map(|context| Context { context }) - } - } -} - -impl GlContext for GlWindow { - unsafe fn make_current(&self) -> Result<(), ContextError> { - self.context.make_current() - } - - fn is_current(&self) -> bool { - self.context.is_current() - } - - fn get_proc_address(&self, addr: &str) -> *const () { - self.context.get_proc_address(addr) - } - - fn get_api(&self) -> Api { - self.context.get_api() - } -} - -impl std::ops::Deref for GlWindow { - type Target = Window; - fn deref(&self) -> &Self::Target { - &self.window + /// Builds a separated context. + pub fn build_separated( + self, + win: &Window, + el: &EventsLoop, + ) -> Result { + SeparatedContext::new(win, self, el) } } @@ -506,21 +326,31 @@ impl CreationError { CreationError::OsError(ref text) => &text, CreationError::NotSupported(text) => &text, CreationError::NoBackendAvailable(_) => "No backend is available", - CreationError::RobustnessNotSupported => "You requested robustness, but it is \ - not supported.", - CreationError::OpenGlVersionNotSupported => "The requested OpenGL version is not \ - supported.", - CreationError::NoAvailablePixelFormat => "Couldn't find any pixel format that matches \ - the criteria.", + CreationError::RobustnessNotSupported => { + "You requested robustness, but it is not supported." + } + CreationError::OpenGlVersionNotSupported => { + "The requested OpenGL version is not supported." + } + CreationError::NoAvailablePixelFormat => { + "Couldn't find any pixel format that matches the criteria." + } CreationError::PlatformSpecific(ref text) => &text, - CreationError::Window(ref err) => std::error::Error::description(err), - CreationError::CreationErrorPair(ref _err1, ref _err2) => "Received two errors." + CreationError::Window(ref err) => { + std::error::Error::description(err) + } + CreationError::CreationErrorPair(ref _err1, ref _err2) => { + "Received two errors." + } } } } impl std::fmt::Display for CreationError { - fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt( + &self, + formatter: &mut std::fmt::Formatter, + ) -> Result<(), std::fmt::Error> { formatter.write_str(self.to_string())?; if let CreationError::CreationErrorPair(ref e1, ref e2) = *self { @@ -535,7 +365,7 @@ impl std::fmt::Display for CreationError { if let &CreationError::NotSupported(msg) = self { write!(formatter, ": {}", msg)?; } - if let Some(err) = std::error::Error::cause(self) { + if let Some(err) = std::error::Error::source(self) { write!(formatter, ": {}", err)?; } Ok(()) @@ -551,7 +381,7 @@ impl std::error::Error for CreationError { match *self { CreationError::NoBackendAvailable(ref err) => Some(&**err), CreationError::Window(ref err) => Some(err), - _ => None + _ => None, } } } @@ -577,13 +407,16 @@ impl ContextError { match *self { ContextError::OsError(ref string) => string, ContextError::IoError(ref err) => err.description(), - ContextError::ContextLost => "Context lost" + ContextError::ContextLost => "Context lost", } } } impl std::fmt::Display for ContextError { - fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + fn fmt( + &self, + formatter: &mut std::fmt::Formatter, + ) -> Result<(), std::fmt::Error> { formatter.write_str(self.to_string()) } } @@ -614,7 +447,8 @@ pub enum GlProfile { Core, } -/// Describes the OpenGL API and version that are being requested when a context is created. +/// Describes the OpenGL API and version that are being requested when a context +/// is created. #[derive(Debug, Copy, Clone)] pub enum GlRequest { /// Request the latest version of the "best" API of this platform. @@ -627,9 +461,9 @@ pub enum GlRequest { /// Example: `GlRequest::Specific(Api::OpenGl, (3, 3))`. Specific(Api, (u8, u8)), - /// If OpenGL is available, create an OpenGL context with the specified `opengl_version`. - /// Else if OpenGL ES or WebGL is available, create a context with the - /// specified `opengles_version`. + /// If OpenGL is available, create an OpenGL context with the specified + /// `opengl_version`. Else if OpenGL ES or WebGL is available, create a + /// context with the specified `opengles_version`. GlThenGles { /// The version to use for OpenGL. opengl_version: (u8, u8), @@ -643,7 +477,10 @@ impl GlRequest { pub fn to_gl_version(&self) -> Option<(u8, u8)> { match self { &GlRequest::Specific(Api::OpenGl, version) => Some(version), - &GlRequest::GlThenGles { opengl_version: version, .. } => Some(version), + &GlRequest::GlThenGles { + opengl_version: version, + .. + } => Some(version), _ => None, } } @@ -654,37 +491,41 @@ 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. +/// 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. + /// 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. + /// 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 optimization, no error will be returned if the backend - /// doesn't support it. Instead it will automatically fall back to `NotRobust`. + /// Since this option is purely an optimization, 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. + /// 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. + /// 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 :-/ + /// 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. + /// Same as `RobustLoseContextOnReset` but the context creation doesn't + /// fail if it's not supported. TryRobustLoseContextOnReset, } @@ -694,7 +535,8 @@ pub enum ReleaseBehavior { /// Doesn't do anything. Most notably doesn't flush. None, - /// Flushes the context that was previously current as if `glFlush` was called. + /// Flushes the context that was previously current as if `glFlush` was + /// called. Flush, } @@ -717,21 +559,24 @@ pub struct PixelFormat { // TODO: swap method? (swap, copy) #[derive(Clone, Debug)] pub struct PixelFormatRequirements { - /// If true, only hardware-accelerated formats will be considered. If false, only software - /// renderers. `None` means "don't care". Default is `Some(true)`. + /// If true, only hardware-accelerated formats will be considered. If + /// false, only software renderers. `None` means "don't care". Default + /// is `Some(true)`. pub hardware_accelerated: Option, - /// Minimum number of bits for the color buffer, excluding alpha. `None` means "don't care". - /// The default is `Some(24)`. + /// Minimum number of bits for the color buffer, excluding alpha. `None` + /// means "don't care". The default is `Some(24)`. pub color_bits: Option, - /// If true, the color buffer must be in a floating point format. Default is `false`. + /// If true, the color buffer must be in a floating point format. Default + /// is `false`. /// - /// Using floating points allows you to write values outside of the `[0.0, 1.0]` range. + /// Using floating points allows you to write values outside of the `[0.0, + /// 1.0]` range. pub float_color_buffer: bool, - /// Minimum number of bits for the alpha in the color buffer. `None` means "don't care". - /// The default is `Some(8)`. + /// Minimum number of bits for the alpha in the color buffer. `None` means + /// "don't care". The default is `Some(8)`. pub alpha_bits: Option, /// Minimum number of bits for the depth buffer. `None` means "don't care". @@ -742,25 +587,30 @@ pub struct PixelFormatRequirements { /// The default value is `Some(8)`. pub stencil_bits: Option, - /// If true, only double-buffered formats will be considered. If false, only single-buffer - /// formats. `None` means "don't care". The default is `Some(true)`. + /// If true, only double-buffered formats will be considered. If false, + /// only single-buffer formats. `None` means "don't care". The default + /// is `Some(true)`. pub double_buffer: Option, - /// Contains the minimum number of samples per pixel in the color, depth and stencil buffers. - /// `None` means "don't care". Default is `None`. + /// Contains the minimum number of samples per pixel in the color, depth + /// and stencil buffers. `None` means "don't care". Default is `None`. /// A value of `Some(0)` indicates that multisampling must not be enabled. pub multisampling: Option, - /// If true, only stereoscopic formats will be considered. If false, only non-stereoscopic - /// formats. The default is `false`. + /// If true, only stereoscopic formats will be considered. If false, only + /// non-stereoscopic formats. The default is `false`. pub stereoscopy: bool, - /// If true, only sRGB-capable formats will be considered. If false, don't care. - /// The default is `false`. + /// If true, only sRGB-capable formats will be considered. If false, don't + /// care. The default is `false`. pub srgb: bool, /// The behavior when changing the current context. Default is `Flush`. pub release_behavior: ReleaseBehavior, + + /// X11 only: set internally to insure a certain visual xid is used when + /// choosing the fbconfig. + pub(crate) x11_visual_xid: Option, } impl Default for PixelFormatRequirements { @@ -778,6 +628,7 @@ impl Default for PixelFormatRequirements { stereoscopy: false, srgb: false, release_behavior: ReleaseBehavior::Flush, + x11_visual_xid: None, } } } @@ -809,12 +660,14 @@ pub struct GlAttributes { /// How the OpenGL context should detect errors. /// - /// The default is `NotRobust` because this is what is typically expected when you create an - /// OpenGL context. However for safety you should consider `TryRobustLoseContextOnReset`. + /// The default is `NotRobust` because this is what is typically expected + /// when you create an OpenGL context. However for safety you should + /// consider `TryRobustLoseContextOnReset`. pub robustness: Robustness, - /// Whether to use vsync. If vsync is enabled, calling `swap_buffers` will block until the - /// screen refreshes. This is typically used to prevent screen tearing. + /// Whether to use vsync. If vsync is enabled, calling `swap_buffers` will + /// block until the screen refreshes. This is typically used to prevent + /// screen tearing. /// /// The default is `false`. pub vsync: bool, @@ -823,7 +676,10 @@ pub struct GlAttributes { impl GlAttributes { /// Turns the `sharing` parameter into another type by calling a closure. #[inline] - pub fn map_sharing(self, f: F) -> GlAttributes where F: FnOnce(S) -> T { + pub fn map_sharing(self, f: F) -> GlAttributes + where + F: FnOnce(S) -> T, + { GlAttributes { sharing: self.sharing.map(f), version: self.version, diff --git a/src/os/android.rs b/src/os/android.rs index be72c4a89a..70ed8041ad 100644 --- a/src/os/android.rs +++ b/src/os/android.rs @@ -4,12 +4,12 @@ pub use winit::os::android::{WindowBuilderExt, WindowExt}; pub use api::egl::ffi::EGLContext; +use os::ContextTraitExt; use Context; -use os::GlContextExt; use std::os::raw; -impl GlContextExt for Context { +impl ContextTraitExt for Context { type Handle = EGLContext; #[inline] diff --git a/src/os/ios.rs b/src/os/ios.rs index 10fd83f254..ebdf1e014d 100644 --- a/src/os/ios.rs +++ b/src/os/ios.rs @@ -1,3 +1,3 @@ #![cfg(target_os = "ios")] -pub use winit::os::ios::{MonitorIdExt, WindowExt, WindowBuilderExt}; +pub use winit::os::ios::{MonitorIdExt, WindowBuilderExt, WindowExt}; diff --git a/src/os/macos.rs b/src/os/macos.rs index 99957491e2..0096eba2c7 100644 --- a/src/os/macos.rs +++ b/src/os/macos.rs @@ -5,12 +5,12 @@ pub use winit::os::macos::MonitorIdExt; pub use winit::os::macos::WindowBuilderExt; pub use winit::os::macos::WindowExt; +use os::ContextTraitExt; use Context; -use os::GlContextExt; use std::os::raw::c_void; -impl GlContextExt for Context { +impl ContextTraitExt for Context { type Handle = *mut c_void; #[inline] diff --git a/src/os/mod.rs b/src/os/mod.rs index 9e738e4290..c0ac2b4bf5 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -18,17 +18,17 @@ pub mod windows; use std::os::raw; /// Platform-specific extensions for OpenGL contexts. -pub trait GlContextExt { +pub trait ContextTraitExt { /// Raw context handle. type Handle; /// Returns the raw context handle. unsafe fn raw_handle(&self) -> Self::Handle; - /// Returns a pointer to the `EGLDisplay` object of EGL that is used by this context. + /// Returns a pointer to the `EGLDisplay` object of EGL that is used by this + /// context. /// /// Return `None` if the context doesn't use EGL. - // - // The pointer will become invalid when the glutin `GlContext` is destroyed. + // The pointer will become invalid when the context is destroyed. unsafe fn get_egl_display(&self) -> Option<*const raw::c_void>; } diff --git a/src/os/unix.rs b/src/os/unix.rs index 6bf06bbd9f..58619edd0b 100644 --- a/src/os/unix.rs +++ b/src/os/unix.rs @@ -1,22 +1,28 @@ -#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] +#![cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] pub use api::egl::ffi::EGLContext; pub use api::glx::ffi::GLXContext; pub use platform::RawHandle; -pub use winit::os::unix::XNotSupported; -pub use winit::os::unix::XWindowType; pub use winit::os::unix::EventsLoopExt; pub use winit::os::unix::MonitorIdExt; pub use winit::os::unix::WindowBuilderExt; pub use winit::os::unix::WindowExt; +pub use winit::os::unix::XNotSupported; +pub use winit::os::unix::XWindowType; +use os::ContextTraitExt; use Context; -use os::GlContextExt; use std::os::raw; -impl GlContextExt for Context { +impl ContextTraitExt for Context { type Handle = RawHandle; #[inline] diff --git a/src/os/windows.rs b/src/os/windows.rs index ee5f49df8a..0bd866431f 100644 --- a/src/os/windows.rs +++ b/src/os/windows.rs @@ -1,17 +1,19 @@ #![cfg(target_os = "windows")] pub use winapi::shared::windef::HGLRC; -pub use winit::os::windows::{DeviceIdExt, MonitorIdExt, WindowBuilderExt, WindowExt}; +pub use winit::os::windows::{ + DeviceIdExt, MonitorIdExt, WindowBuilderExt, WindowExt, +}; pub use api::egl::ffi::EGLContext; pub use platform::RawHandle; use std::os::raw; -use os::GlContextExt; +use os::ContextTraitExt; use Context; -impl GlContextExt for Context { +impl ContextTraitExt for Context { type Handle = RawHandle; #[inline] diff --git a/src/platform/emscripten/ffi.rs b/src/platform/emscripten/ffi.rs index 40e9b692e4..c8065c6d7c 100644 --- a/src/platform/emscripten/ffi.rs +++ b/src/platform/emscripten/ffi.rs @@ -9,10 +9,13 @@ pub type EM_UTF8 = libc::c_char; pub type EMSCRIPTEN_WEBGL_CONTEXT_HANDLE = libc::c_int; pub type EMSCRIPTEN_RESULT = libc::c_int; -pub type em_webgl_context_callback = extern fn(libc::c_int, *const libc::c_void, *mut libc::c_void) - -> EM_BOOL; +pub type em_webgl_context_callback = extern "C" fn( + libc::c_int, + *const libc::c_void, + *mut libc::c_void, +) -> EM_BOOL; -pub type em_callback_func = unsafe extern fn(); +pub type em_callback_func = unsafe extern "C" fn(); #[repr(C)] #[derive(Debug)] @@ -42,47 +45,78 @@ pub const EMSCRIPTEN_RESULT_INVALID_PARAM: libc::c_int = -5; pub const EMSCRIPTEN_RESULT_FAILED: libc::c_int = -6; pub const EMSCRIPTEN_RESULT_NO_DATA: libc::c_int = -7; -extern { - pub fn emscripten_webgl_init_context_attributes(attributes: *mut EmscriptenWebGLContextAttributes); - pub fn emscripten_webgl_create_context(target: *const libc::c_char, - attributes: *const EmscriptenWebGLContextAttributes) -> EMSCRIPTEN_WEBGL_CONTEXT_HANDLE; - - pub fn emscripten_webgl_make_context_current(context: EMSCRIPTEN_WEBGL_CONTEXT_HANDLE) - -> EMSCRIPTEN_RESULT; - - pub fn emscripten_webgl_get_current_context() -> EMSCRIPTEN_WEBGL_CONTEXT_HANDLE; - - pub fn emscripten_webgl_destroy_context(context: EMSCRIPTEN_WEBGL_CONTEXT_HANDLE) - -> EMSCRIPTEN_RESULT; - - pub fn emscripten_webgl_enable_extension(context: EMSCRIPTEN_WEBGL_CONTEXT_HANDLE, - extension: *const libc::c_char) -> EM_BOOL; - - pub fn emscripten_set_webglcontextlost_callback(target: *const libc::c_char, - userData: *mut libc::c_void, useCapture: EM_BOOL, callback: em_webgl_context_callback) - -> EMSCRIPTEN_RESULT; - pub fn emscripten_set_webglcontextrestored_callback(target: *const libc::c_char, - userData: *mut libc::c_void, useCapture: EM_BOOL, callback: em_webgl_context_callback) - -> EMSCRIPTEN_RESULT; - - pub fn emscripten_is_webgl_context_lost(target: *const libc::c_char) -> EM_BOOL; - - // note: this function is not documented but is used by the ports of glfw, SDL and EGL - pub fn emscripten_GetProcAddress(name: *const libc::c_char) -> *const libc::c_void; - - - pub fn emscripten_request_fullscreen(target: *const libc::c_char, - deferUntilInEventHandler: EM_BOOL) -> EMSCRIPTEN_RESULT; +extern "C" { + pub fn emscripten_webgl_init_context_attributes( + attributes: *mut EmscriptenWebGLContextAttributes, + ); + pub fn emscripten_webgl_create_context( + target: *const libc::c_char, + attributes: *const EmscriptenWebGLContextAttributes, + ) -> EMSCRIPTEN_WEBGL_CONTEXT_HANDLE; + + pub fn emscripten_webgl_make_context_current( + context: EMSCRIPTEN_WEBGL_CONTEXT_HANDLE, + ) -> EMSCRIPTEN_RESULT; + + pub fn emscripten_webgl_get_current_context( + ) -> EMSCRIPTEN_WEBGL_CONTEXT_HANDLE; + + pub fn emscripten_webgl_destroy_context( + context: EMSCRIPTEN_WEBGL_CONTEXT_HANDLE, + ) -> EMSCRIPTEN_RESULT; + + pub fn emscripten_webgl_enable_extension( + context: EMSCRIPTEN_WEBGL_CONTEXT_HANDLE, + extension: *const libc::c_char, + ) -> EM_BOOL; + + pub fn emscripten_set_webglcontextlost_callback( + target: *const libc::c_char, + userData: *mut libc::c_void, + useCapture: EM_BOOL, + callback: em_webgl_context_callback, + ) -> EMSCRIPTEN_RESULT; + pub fn emscripten_set_webglcontextrestored_callback( + target: *const libc::c_char, + userData: *mut libc::c_void, + useCapture: EM_BOOL, + callback: em_webgl_context_callback, + ) -> EMSCRIPTEN_RESULT; + + pub fn emscripten_is_webgl_context_lost( + target: *const libc::c_char, + ) -> EM_BOOL; + + // note: this function is not documented but is used by the ports of glfw, + // SDL and EGL + pub fn emscripten_GetProcAddress( + name: *const libc::c_char, + ) -> *const libc::c_void; + + pub fn emscripten_request_fullscreen( + target: *const libc::c_char, + deferUntilInEventHandler: EM_BOOL, + ) -> EMSCRIPTEN_RESULT; pub fn emscripten_exit_fullscreen() -> EMSCRIPTEN_RESULT; - pub fn emscripten_set_element_css_size(target: *const libc::c_char, width: libc::c_double, - height: libc::c_double) -> EMSCRIPTEN_RESULT; + pub fn emscripten_set_element_css_size( + target: *const libc::c_char, + width: libc::c_double, + height: libc::c_double, + ) -> EMSCRIPTEN_RESULT; - pub fn emscripten_get_element_css_size(target: *const libc::c_char, width: *mut libc::c_double, - height: *mut libc::c_double) -> EMSCRIPTEN_RESULT; + pub fn emscripten_get_element_css_size( + target: *const libc::c_char, + width: *mut libc::c_double, + height: *mut libc::c_double, + ) -> EMSCRIPTEN_RESULT; pub fn emscripten_sleep(delay: libc::c_uint); - pub fn emscripten_set_main_loop(func : em_callback_func, fps : libc::c_int, simulate_infinite_loop : libc::c_int); + pub fn emscripten_set_main_loop( + func: em_callback_func, + fps: libc::c_int, + simulate_infinite_loop: libc::c_int, + ); } diff --git a/src/platform/emscripten/mod.rs b/src/platform/emscripten/mod.rs index a1c8f181ee..ac5d139086 100644 --- a/src/platform/emscripten/mod.rs +++ b/src/platform/emscripten/mod.rs @@ -17,26 +17,29 @@ pub enum Context { impl Context { #[inline] pub fn new( - window_builder: winit::WindowBuilder, - events_loop: &winit::EventsLoop, + wb: winit::WindowBuilder, + el: &winit::EventsLoop, _pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Context>, - ) -> Result<(winit::Window, Self), CreationError> - { - let window = window_builder.build(events_loop)?; + ) -> Result<(winit::Window, Self), CreationError> { + let window = wb.build(el)?; - let gl_attr = gl_attr.clone().map_sharing(|_| unimplemented!("Shared contexts are unimplemented in WebGL.")); + let gl_attr = gl_attr.clone().map_sharing(|_| { + unimplemented!("Shared contexts are unimplemented in WebGL.") + }); // getting the default values of attributes let mut attributes = unsafe { use std::mem; - let mut attributes: ffi::EmscriptenWebGLContextAttributes = mem::uninitialized(); + let mut attributes: ffi::EmscriptenWebGLContextAttributes = + mem::uninitialized(); ffi::emscripten_webgl_init_context_attributes(&mut attributes); attributes }; // setting the attributes - if let GlRequest::Specific(Api::WebGl, (major, minor)) = gl_attr.version { + if let GlRequest::Specific(Api::WebGl, (major, minor)) = gl_attr.version + { attributes.majorVersion = major as _; attributes.minorVersion = minor as _; } @@ -45,10 +48,13 @@ impl Context { let context = unsafe { use std::{mem, ptr}; // TODO: correct first parameter based on the window - let context = ffi::emscripten_webgl_create_context(ptr::null(), &attributes); + let context = + ffi::emscripten_webgl_create_context(ptr::null(), &attributes); if context <= 0 { - return Err(CreationError::OsError(format!("Error while calling emscripten_webgl_create_context: {}", - error_to_str(mem::transmute(context))))); + return Err(CreationError::OsError(format!( + "Error while calling emscripten_webgl_create_context: {}", + error_to_str(mem::transmute(context)) + ))); } context }; @@ -63,9 +69,7 @@ impl Context { el: &winit::EventsLoop, pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Context>, - shareable_with_windowed_contexts: bool, ) -> Result { - assert!(!shareable_with_windowed_contexts); // TODO: Implement if possible let wb = winit::WindowBuilder::new().with_visibility(false); Self::new(wb, el, pf_reqs, gl_attr).map(|(w, c)| match c { Context::Window(c) => Context::WindowedContext(w, c), @@ -73,6 +77,17 @@ impl Context { }) } + /// See the docs in the crate root file. + #[inline] + pub fn new_separated( + _window: &winit::Window, + _el: &winit::EventsLoop, + _pf_reqs: &PixelFormatRequirements, + _gl_attr: &GlAttributes<&Context>, + ) -> Result { + unimplemented!() + } + #[inline] pub fn resize(&self, _width: u32, _height: u32) { match self { @@ -100,8 +115,10 @@ impl Context { let addr = CString::new(addr).unwrap(); unsafe { - // FIXME: if `as_ptr()` is used, then wrong data is passed to emscripten - ffi::emscripten_GetProcAddress(addr.into_raw() as *const _) as *const _ + // FIXME: if `as_ptr()` is used, then wrong data is passed to + // emscripten + ffi::emscripten_GetProcAddress(addr.into_raw() as *const _) + as *const _ } } @@ -150,8 +167,9 @@ impl Drop for Context { fn error_to_str(code: ffi::EMSCRIPTEN_RESULT) -> &'static str { match code { - ffi::EMSCRIPTEN_RESULT_SUCCESS | ffi::EMSCRIPTEN_RESULT_DEFERRED - => "Internal error in the library (success detected as failure)", + ffi::EMSCRIPTEN_RESULT_SUCCESS | ffi::EMSCRIPTEN_RESULT_DEFERRED => { + "Internal error in the library (success detected as failure)" + } ffi::EMSCRIPTEN_RESULT_NOT_SUPPORTED => "Not supported", ffi::EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED => "Failed not deferred", @@ -161,6 +179,6 @@ fn error_to_str(code: ffi::EMSCRIPTEN_RESULT) -> &'static str { ffi::EMSCRIPTEN_RESULT_FAILED => "Failed", ffi::EMSCRIPTEN_RESULT_NO_DATA => "No data", - _ => "Undocumented error" + _ => "Undocumented error", } } diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 5cba6a294f..4f55b6147c 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -1,9 +1,18 @@ -#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] +#![cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] -use {ContextError, CreationError, GlAttributes, PixelFormat, PixelFormatRequirements}; +use self::x11::X11Context; use api::egl; use api::glx; -use self::x11::GlContext; +use { + ContextError, CreationError, GlAttributes, PixelFormat, + PixelFormatRequirements, +}; use winit; use winit::os::unix::EventsLoopExt; @@ -36,36 +45,36 @@ pub enum Context { } impl Context { - fn is_compatible(c: &Option<&Context>, ct: ContextType) -> Result<(), CreationError> { + fn is_compatible( + c: &Option<&Context>, + ct: ContextType, + ) -> Result<(), CreationError> { if let Some(c) = *c { match ct { - ContextType::OsMesa => { - match *c { - Context::OsMesa(_) => Ok(()), - _ => { - let msg = "Cannot share an OSMesa context with a non-OSMesa context"; - return Err(CreationError::PlatformSpecific(msg.into())); - } + ContextType::OsMesa => match *c { + Context::OsMesa(_) => Ok(()), + _ => { + let msg = "Cannot share an OSMesa context with a non-OSMesa context"; + return Err(CreationError::PlatformSpecific(msg.into())); } - } - ContextType::X11 => { - match *c { - Context::WindowedX11(_) | Context::HeadlessX11(_, _) => Ok(()), - _ => { - let msg = "Cannot share an X11 context with a non-X11 context"; - return Err(CreationError::PlatformSpecific(msg.into())); - } + }, + ContextType::X11 => match *c { + Context::WindowedX11(_) | Context::HeadlessX11(_, _) => { + Ok(()) } - } - ContextType::Wayland => { - match *c { - Context::WindowedWayland(_) | Context::HeadlessWayland(_, _) => Ok(()), - _ => { - let msg = "Cannot share a Wayland context with a non-Wayland context"; - return Err(CreationError::PlatformSpecific(msg.into())); - } + _ => { + let msg = "Cannot share an X11 context with a non-X11 context"; + return Err(CreationError::PlatformSpecific(msg.into())); } - } + }, + ContextType::Wayland => match *c { + Context::WindowedWayland(_) + | Context::HeadlessWayland(_, _) => Ok(()), + _ => { + let msg = "Cannot share a Wayland context with a non-Wayland context"; + return Err(CreationError::PlatformSpecific(msg.into())); + } + }, } } else { Ok(()) @@ -73,69 +82,92 @@ impl Context { } #[inline] - pub unsafe fn new( - window_builder: winit::WindowBuilder, - events_loop: &winit::EventsLoop, + pub fn new( + wb: winit::WindowBuilder, + el: &winit::EventsLoop, pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Context>, - ) -> Result<(winit::Window, Self), CreationError> - { - if events_loop.is_wayland() { + ) -> Result<(winit::Window, Self), CreationError> { + if el.is_wayland() { Context::is_compatible(&gl_attr.sharing, ContextType::Wayland)?; - let gl_attr = gl_attr.clone().map_sharing(|ctxt| match ctxt { - &Context::WindowedWayland(ref ctxt) | &Context::HeadlessWayland(_, ref ctxt) => ctxt, + let gl_attr = gl_attr.clone().map_sharing(|ctx| match ctx { + &Context::WindowedWayland(ref ctx) + | &Context::HeadlessWayland(_, ref ctx) => ctx, _ => unreachable!(), }); - wayland::Context::new(window_builder, events_loop, pf_reqs, &gl_attr) - .map(|(window, context)| (window, Context::WindowedWayland(context))) + wayland::Context::new(wb, el, pf_reqs, &gl_attr).map( + |(window, context)| (window, Context::WindowedWayland(context)), + ) } else { Context::is_compatible(&gl_attr.sharing, ContextType::X11)?; - let gl_attr = gl_attr.clone().map_sharing(|ctxt| match ctxt { - &Context::WindowedX11(ref ctxt) | &Context::HeadlessX11(_, ref ctxt) => ctxt, + let gl_attr = gl_attr.clone().map_sharing(|ctx| match ctx { + &Context::WindowedX11(ref ctx) + | &Context::HeadlessX11(_, ref ctx) => ctx, _ => unreachable!(), }); - x11::Context::new(window_builder, events_loop, pf_reqs, &gl_attr) - .map(|(window, context)| (window, Context::WindowedX11(context))) + x11::Context::new(wb, el, pf_reqs, &gl_attr).map( + |(window, context)| (window, Context::WindowedX11(context)), + ) } } #[inline] - pub unsafe fn new_context( + pub fn new_context( el: &winit::EventsLoop, pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Context>, - shareable_with_windowed_contexts: bool, - ) -> Result - { - if shareable_with_windowed_contexts { - let wb = winit::WindowBuilder::new().with_visibility(false); - - if el.is_wayland() { - Context::is_compatible(&gl_attr.sharing, ContextType::Wayland)?; - let gl_attr = gl_attr.clone().map_sharing(|ctxt| match ctxt { - &Context::WindowedWayland(ref ctxt) | &Context::HeadlessWayland(_, ref ctxt) => ctxt, - _ => unreachable!(), - }); - wayland::Context::new(wb, &el, pf_reqs, &gl_attr) - .map(|(window, context)| Context::HeadlessWayland(window, context)) - } else { - Context::is_compatible(&gl_attr.sharing, ContextType::X11)?; - let gl_attr = gl_attr.clone().map_sharing(|ctxt| match ctxt { - &Context::WindowedX11(ref ctxt) | &Context::HeadlessX11(_, ref ctxt) => ctxt, - _ => unreachable!(), - }); - x11::Context::new(wb, &el, pf_reqs, &gl_attr) - .map(|(window, context)| Context::HeadlessX11(window, context)) - } + ) -> Result { + let wb = winit::WindowBuilder::new().with_visibility(false); + + if el.is_wayland() { + Context::is_compatible(&gl_attr.sharing, ContextType::Wayland)?; + let gl_attr = gl_attr.clone().map_sharing(|ctx| match ctx { + &Context::WindowedWayland(ref ctx) + | &Context::HeadlessWayland(_, ref ctx) => ctx, + _ => unreachable!(), + }); + wayland::Context::new(wb, &el, pf_reqs, &gl_attr).map( + |(window, context)| Context::HeadlessWayland(window, context), + ) + } else { + Context::is_compatible(&gl_attr.sharing, ContextType::X11)?; + let gl_attr = gl_attr.clone().map_sharing(|ctx| match ctx { + &Context::WindowedX11(ref ctx) + | &Context::HeadlessX11(_, ref ctx) => ctx, + _ => unreachable!(), + }); + x11::Context::new(wb, &el, pf_reqs, &gl_attr) + .map(|(window, context)| Context::HeadlessX11(window, context)) + } + } + + #[inline] + pub fn new_separated( + window: &winit::Window, + el: &winit::EventsLoop, + pf_reqs: &PixelFormatRequirements, + gl_attr: &GlAttributes<&Context>, + ) -> Result { + if el.is_wayland() { + Context::is_compatible(&gl_attr.sharing, ContextType::Wayland)?; + + let gl_attr = gl_attr.clone().map_sharing(|ctx| match ctx { + &Context::WindowedWayland(ref ctx) + | &Context::HeadlessWayland(_, ref ctx) => ctx, + _ => unreachable!(), + }); + wayland::Context::new_separated(window, el, pf_reqs, &gl_attr) + .map(|context| Context::WindowedWayland(context)) } else { - Context::is_compatible(&gl_attr.sharing, ContextType::OsMesa)?; - let gl_attr = gl_attr.clone().map_sharing(|ctxt| match ctxt { - &Context::OsMesa(ref ctxt) => ctxt, + Context::is_compatible(&gl_attr.sharing, ContextType::X11)?; + let gl_attr = gl_attr.clone().map_sharing(|ctx| match ctx { + &Context::WindowedX11(ref ctx) + | &Context::HeadlessX11(_, ref ctx) => ctx, _ => unreachable!(), }); - osmesa::OsMesaContext::new((1, 1), pf_reqs, &gl_attr) - .map(|context| Context::OsMesa(context)) + x11::Context::new_separated(window, el, pf_reqs, &gl_attr) + .map(|context| Context::WindowedX11(context)) } } @@ -143,7 +175,7 @@ impl Context { pub fn resize(&self, width: u32, height: u32) { match *self { Context::WindowedX11(_) => (), - Context::WindowedWayland(ref ctxt) => ctxt.resize(width, height), + Context::WindowedWayland(ref ctx) => ctx.resize(width, height), _ => unreachable!(), } } @@ -151,35 +183,43 @@ impl Context { #[inline] pub unsafe fn make_current(&self) -> Result<(), ContextError> { match *self { - Context::WindowedX11(ref ctxt) | Context::HeadlessX11(_, ref ctxt) => ctxt.make_current(), - Context::WindowedWayland(ref ctxt) | Context::HeadlessWayland(_, ref ctxt) => ctxt.make_current(), - Context::OsMesa(ref ctxt) => ctxt.make_current(), + Context::WindowedX11(ref ctx) + | Context::HeadlessX11(_, ref ctx) => ctx.make_current(), + Context::WindowedWayland(ref ctx) + | Context::HeadlessWayland(_, ref ctx) => ctx.make_current(), + Context::OsMesa(ref ctx) => ctx.make_current(), } } #[inline] pub fn is_current(&self) -> bool { match *self { - Context::WindowedX11(ref ctxt) | Context::HeadlessX11(_, ref ctxt) => ctxt.is_current(), - Context::WindowedWayland(ref ctxt) | Context::HeadlessWayland(_, ref ctxt) => ctxt.is_current(), - Context::OsMesa(ref ctxt) => ctxt.is_current(), + Context::WindowedX11(ref ctx) + | Context::HeadlessX11(_, ref ctx) => ctx.is_current(), + Context::WindowedWayland(ref ctx) + | Context::HeadlessWayland(_, ref ctx) => ctx.is_current(), + Context::OsMesa(ref ctx) => ctx.is_current(), } } #[inline] pub fn get_proc_address(&self, addr: &str) -> *const () { match *self { - Context::WindowedX11(ref ctxt) | Context::HeadlessX11(_, ref ctxt) => ctxt.get_proc_address(addr), - Context::WindowedWayland(ref ctxt) | Context::HeadlessWayland(_, ref ctxt) => ctxt.get_proc_address(addr), - Context::OsMesa(ref ctxt) => ctxt.get_proc_address(addr), + Context::WindowedX11(ref ctx) + | Context::HeadlessX11(_, ref ctx) => ctx.get_proc_address(addr), + Context::WindowedWayland(ref ctx) + | Context::HeadlessWayland(_, ref ctx) => { + ctx.get_proc_address(addr) + } + Context::OsMesa(ref ctx) => ctx.get_proc_address(addr), } } #[inline] pub fn swap_buffers(&self) -> Result<(), ContextError> { match *self { - Context::WindowedX11(ref ctxt) => ctxt.swap_buffers(), - Context::WindowedWayland(ref ctxt) => ctxt.swap_buffers(), + Context::WindowedX11(ref ctx) => ctx.swap_buffers(), + Context::WindowedWayland(ref ctx) => ctx.swap_buffers(), _ => unreachable!(), } } @@ -187,17 +227,19 @@ impl Context { #[inline] pub fn get_api(&self) -> ::Api { match *self { - Context::WindowedX11(ref ctxt) | Context::HeadlessX11(_, ref ctxt) => ctxt.get_api(), - Context::WindowedWayland(ref ctxt) | Context::HeadlessWayland(_, ref ctxt) => ctxt.get_api(), - Context::OsMesa(ref ctxt) => ctxt.get_api(), + Context::WindowedX11(ref ctx) + | Context::HeadlessX11(_, ref ctx) => ctx.get_api(), + Context::WindowedWayland(ref ctx) + | Context::HeadlessWayland(_, ref ctx) => ctx.get_api(), + Context::OsMesa(ref ctx) => ctx.get_api(), } } #[inline] pub fn get_pixel_format(&self) -> PixelFormat { match *self { - Context::WindowedX11(ref ctxt) => ctxt.get_pixel_format(), - Context::WindowedWayland(ref ctxt) => ctxt.get_pixel_format(), + Context::WindowedX11(ref ctx) => ctx.get_pixel_format(), + Context::WindowedWayland(ref ctx) => ctx.get_pixel_format(), _ => unreachable!(), } } @@ -205,26 +247,66 @@ impl Context { #[inline] pub unsafe fn raw_handle(&self) -> RawHandle { match *self { - Context::WindowedX11(ref ctxt) | Context::HeadlessX11(_, ref ctxt) => match *ctxt.raw_handle() { - GlContext::Glx(ref ctxt) => RawHandle::Glx(ctxt.raw_handle()), - GlContext::Egl(ref ctxt) => RawHandle::Egl(ctxt.raw_handle()), - GlContext::None => panic!() + Context::WindowedX11(ref ctx) + | Context::HeadlessX11(_, ref ctx) => match *ctx.raw_handle() { + X11Context::Glx(ref ctx) => RawHandle::Glx(ctx.raw_handle()), + X11Context::Egl(ref ctx) => RawHandle::Egl(ctx.raw_handle()), + X11Context::None => panic!(), }, - Context::WindowedWayland(ref ctxt) | Context::HeadlessWayland(_, ref ctxt) => RawHandle::Egl(ctxt.raw_handle()), - Context::OsMesa(ref ctxt) => RawHandle::Egl(ctxt.raw_handle()), + Context::WindowedWayland(ref ctx) + | Context::HeadlessWayland(_, ref ctx) => { + RawHandle::Egl(ctx.raw_handle()) + } + Context::OsMesa(ref ctx) => RawHandle::Egl(ctx.raw_handle()), } } #[inline] pub unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { match *self { - Context::WindowedX11(ref ctxt) | Context::HeadlessX11(_, ref ctxt) => { - ctxt.get_egl_display() - } - Context::WindowedWayland(ref ctxt) | Context::HeadlessWayland(_, ref ctxt) => { - ctxt.get_egl_display() - } + Context::WindowedX11(ref ctx) + | Context::HeadlessX11(_, ref ctx) => ctx.get_egl_display(), + Context::WindowedWayland(ref ctx) + | Context::HeadlessWayland(_, ref ctx) => ctx.get_egl_display(), _ => None, } } + + #[inline] + fn new_osmesa( + pf_reqs: &PixelFormatRequirements, + gl_attr: &GlAttributes<&Context>, + ) -> Result { + Context::is_compatible(&gl_attr.sharing, ContextType::OsMesa)?; + let gl_attr = gl_attr.clone().map_sharing(|ctx| match ctx { + &Context::OsMesa(ref ctx) => ctx, + _ => unreachable!(), + }); + osmesa::OsMesaContext::new((1, 1), pf_reqs, &gl_attr) + .map(|context| Context::OsMesa(context)) + } +} + +pub trait OsMesaContextExt { + fn new_osmesa(cb: crate::ContextBuilder) -> Result + where + Self: Sized; +} + +impl OsMesaContextExt for crate::Context { + /// Builds the given OsMesa context. + /// + /// Errors can occur if the OpenGL context could not be created. This + /// generally happens because the underlying platform doesn't support a + /// requested feature. + #[inline] + fn new_osmesa(cb: crate::ContextBuilder) -> Result + where + Self: Sized, + { + let crate::ContextBuilder { pf_reqs, gl_attr } = cb; + let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); + Context::new_osmesa(&pf_reqs, &gl_attr) + .map(|context| crate::Context { context }) + } } diff --git a/src/platform/linux/wayland.rs b/src/platform/linux/wayland.rs index 85987d9bb7..1837438fef 100644 --- a/src/platform/linux/wayland.rs +++ b/src/platform/linux/wayland.rs @@ -1,12 +1,13 @@ -use std::sync::Arc; -use std::ffi::CString; +use api::egl::{self, ffi, Context as EglContext}; use std::os::raw; +use std::sync::Arc; +use wayland_client::egl as wegl; use winit; use winit::os::unix::WindowExt; -use {ContextError, CreationError, GlAttributes, PixelFormat, PixelFormatRequirements}; -use api::dlopen; -use api::egl::{self, ffi, Context as EglContext}; -use wayland_client::egl as wegl; +use { + ContextError, CreationError, GlAttributes, PixelFormat, + PixelFormatRequirements, +}; pub struct Context { egl_surface: Arc, @@ -14,42 +15,54 @@ pub struct Context { } impl Context { + #[inline] pub fn new( - window_builder: winit::WindowBuilder, - events_loop: &winit::EventsLoop, + wb: winit::WindowBuilder, + el: &winit::EventsLoop, pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Context>, - ) -> Result<(winit::Window, Self), CreationError> - { - let window = window_builder.build(events_loop)?; + ) -> Result<(winit::Window, Self), CreationError> { + let window = wb.build(el)?; + let context = Self::new_separated(&window, el, pf_reqs, gl_attr)?; + Ok((window, context)) + } + + #[inline] + pub fn new_separated( + window: &winit::Window, + _el: &winit::EventsLoop, + pf_reqs: &PixelFormatRequirements, + gl_attr: &GlAttributes<&Context>, + ) -> Result { let logical_size = window.get_inner_size().unwrap(); let (w, h) = (logical_size.width, logical_size.height); - let surface = window.get_wayland_surface().unwrap(); - let egl_surface = unsafe { wegl::WlEglSurface::new_from_raw(surface as *mut _, w as i32, h as i32) }; - let context = { - let mut libegl = unsafe { dlopen::dlopen(b"libEGL.so.1\0".as_ptr() as *const _, dlopen::RTLD_NOW) }; - if libegl.is_null() { - libegl = unsafe { dlopen::dlopen(b"libEGL.so\0".as_ptr() as *const _, dlopen::RTLD_NOW) }; - } - if libegl.is_null() { - return Err(CreationError::NotSupported("could not find libEGL")); + let surface = window.get_wayland_surface(); + let surface = match surface { + Some(s) => s, + None => { + return Err(CreationError::NotSupported("Wayland not found")); } - let egl = ::api::egl::ffi::egl::Egl::load_with(|sym| { - let sym = CString::new(sym).unwrap(); - unsafe { dlopen::dlsym(libegl, sym.as_ptr()) } - }); - let gl_attr = gl_attr.clone().map_sharing(|_| unimplemented!()); // TODO + }; + let egl_surface = unsafe { + wegl::WlEglSurface::new_from_raw( + surface as *mut _, + w as i32, + h as i32, + ) + }; + let context = { + let gl_attr = gl_attr.clone().map_sharing(|c| &c.context); let native_display = egl::NativeDisplay::Wayland(Some( - window.get_wayland_display().unwrap() as *const _ + window.get_wayland_display().unwrap() as *const _, )); - EglContext::new(egl, pf_reqs, &gl_attr, native_display) + EglContext::new(pf_reqs, &gl_attr, native_display) .and_then(|p| p.finish(egl_surface.ptr() as *const _))? }; let context = Context { egl_surface: Arc::new(egl_surface), context: context, }; - Ok((window, context)) + Ok(context) } #[inline] diff --git a/src/platform/linux/x11.rs b/src/platform/linux/x11.rs index 9e92550721..b591ec0b11 100644 --- a/src/platform/linux/x11.rs +++ b/src/platform/linux/x11.rs @@ -1,20 +1,20 @@ -pub use winit::os::unix::x11::{XError, XNotSupported, XConnection}; +pub use winit::os::unix::x11::{XConnection, XError, XNotSupported}; -use std::{mem, ptr, fmt, error}; -use std::ffi::CString; use std::os::raw; use std::sync::Arc; +use std::{error, fmt, mem, ptr}; use winit; -use winit::os::unix::{EventsLoopExt, WindowExt, WindowBuilderExt}; +use winit::os::unix::{EventsLoopExt, WindowBuilderExt, WindowExt}; -use {Api, ContextError, CreationError, GlAttributes, GlRequest, PixelFormat, PixelFormatRequirements}; +use { + Api, ContextError, CreationError, GlAttributes, GlRequest, PixelFormat, + PixelFormatRequirements, +}; -use api::glx::{ffi, Context as GlxContext}; -use api::{dlopen, egl}; -use api::egl::Context as EglContext; -use api::glx::ffi::glx::Glx; -use api::egl::ffi::egl::Egl; +use api::egl; +use api::egl::{Context as EglContext, EGL}; +use api::glx::{ffi, Context as GlxContext, GLX}; #[derive(Debug)] struct NoX11Connection; @@ -31,59 +31,7 @@ impl fmt::Display for NoX11Connection { } } -struct GlxOrEgl { - glx: Option, - egl: Option, -} - -impl GlxOrEgl { - fn new() -> GlxOrEgl { - // TODO: use something safer than raw "dlopen" - let glx = { - let mut libglx = unsafe { - dlopen::dlopen(b"libGL.so.1\0".as_ptr() as *const _, dlopen::RTLD_NOW) - }; - if libglx.is_null() { - libglx = unsafe { - dlopen::dlopen(b"libGL.so\0".as_ptr() as *const _, dlopen::RTLD_NOW) - }; - } - if libglx.is_null() { - None - } else { - Some(Glx::load_with(|sym| { - let sym = CString::new(sym).unwrap(); - unsafe { dlopen::dlsym(libglx, sym.as_ptr()) } - })) - } - }; - // TODO: use something safer than raw "dlopen" - let egl = { - let mut libegl = unsafe { - dlopen::dlopen(b"libEGL.so.1\0".as_ptr() as *const _, dlopen::RTLD_NOW) - }; - if libegl.is_null() { - libegl = unsafe { - dlopen::dlopen(b"libEGL.so\0".as_ptr() as *const _, dlopen::RTLD_NOW) - }; - } - if libegl.is_null() { - None - } else { - Some(Egl::load_with(|sym| { - let sym = CString::new(sym).unwrap(); - unsafe { dlopen::dlsym(libegl, sym.as_ptr()) } - })) - } - }; - GlxOrEgl { - glx: glx, - egl: egl, - } - } -} - -pub enum GlContext { +pub enum X11Context { Glx(GlxContext), Egl(EglContext), None, @@ -92,7 +40,7 @@ pub enum GlContext { pub struct Context { xconn: Arc, colormap: ffi::Colormap, - context: GlContext, + context: X11Context, } unsafe impl Send for Context {} @@ -101,9 +49,9 @@ 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 - self.context = GlContext::None; + // we don't call MakeCurrent(0, 0) because we are not sure that the + // context is still the current one + self.context = X11Context::None; (self.xconn.xlib.XFreeColormap)(self.xconn.display, self.colormap); } @@ -111,17 +59,20 @@ impl Drop for Context { } impl Context { - + #[inline] pub fn new( - window_builder: winit::WindowBuilder, - events_loop: &winit::EventsLoop, + wb: winit::WindowBuilder, + el: &winit::EventsLoop, pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Context>, - ) -> Result<(winit::Window, Self), CreationError> - { - let xconn = match events_loop.get_xlib_xconnection() { + ) -> Result<(winit::Window, Self), CreationError> { + let xconn = match el.get_xlib_xconnection() { Some(xconn) => xconn, - None => return Err(CreationError::NoBackendAvailable(Box::new(NoX11Connection))), + None => { + return Err(CreationError::NoBackendAvailable(Box::new( + NoX11Connection, + ))); + } }; // Get the screen_id for the window being built. @@ -133,89 +84,117 @@ impl Context { Egl(::api::egl::ContextPrototype<'a>), } - let builder_clone_opengl_glx = gl_attr.clone().map_sharing(|_| unimplemented!()); // FIXME: - let builder_clone_opengl_egl = gl_attr.clone().map_sharing(|_| unimplemented!()); // FIXME: - let backend = GlxOrEgl::new(); + let builder = gl_attr.clone(); + + let builder_glx_u; + let builder_egl_u; + let context = match gl_attr.version { - GlRequest::Latest | - GlRequest::Specific(Api::OpenGl, _) | - GlRequest::GlThenGles { .. } => { + GlRequest::Latest + | GlRequest::Specific(Api::OpenGl, _) + | GlRequest::GlThenGles { .. } => { // GLX should be preferred over EGL, otherwise crashes may occur // on X11 – issue #314 - if let Some(ref glx) = backend.glx { + if let Some(_) = *GLX { + builder_glx_u = builder.map_sharing(|c| match c.context { + X11Context::Glx(ref c) => c, + _ => panic!(), + }); Prototype::Glx(GlxContext::new( - glx.clone(), Arc::clone(&xconn), pf_reqs, - &builder_clone_opengl_glx, + &builder_glx_u, screen_id, - window_builder.window.transparent, + wb.window.transparent, )?) - } else if let Some(ref egl) = backend.egl { - let native_display = egl::NativeDisplay::X11(Some(xconn.display as *const _)); + } else if let Some(_) = *EGL { + builder_egl_u = builder.map_sharing(|c| match c.context { + X11Context::Egl(ref c) => c, + _ => panic!(), + }); + let native_display = egl::NativeDisplay::X11(Some( + xconn.display as *const _, + )); Prototype::Egl(EglContext::new( - egl.clone(), pf_reqs, - &builder_clone_opengl_egl, + &builder_egl_u, native_display, )?) } else { - return Err(CreationError::NotSupported("both libglx and libEGL not present")); + return Err(CreationError::NotSupported( + "both libglx and libEGL not present", + )); } - }, + } GlRequest::Specific(Api::OpenGlEs, _) => { - if let Some(ref egl) = backend.egl { + if let Some(_) = *EGL { + builder_egl_u = builder.map_sharing(|c| match c.context { + X11Context::Egl(ref c) => c, + _ => panic!(), + }); Prototype::Egl(EglContext::new( - egl.clone(), pf_reqs, - &builder_clone_opengl_egl, - egl::NativeDisplay::X11(Some(xconn.display as *const _)), + &builder_egl_u, + egl::NativeDisplay::X11(Some( + xconn.display as *const _, + )), )?) } else { - return Err(CreationError::NotSupported("libEGL not present")); + return Err(CreationError::NotSupported( + "libEGL not present", + )); } - }, + } GlRequest::Specific(_, _) => { - return Err(CreationError::NotSupported("requested specific without gl or gles")); - }, + return Err(CreationError::NotSupported( + "requested specific without gl or gles", + )); + } }; - // getting the `visual_infos` (a struct that contains information about the visual to use) + // getting the `visual_infos` (a struct that contains information about + // the visual to use) let visual_infos = match context { Prototype::Glx(ref p) => p.get_visual_infos().clone(), Prototype::Egl(ref p) => { - unsafe { - let mut template: ffi::XVisualInfo = mem::zeroed(); - template.visualid = p.get_native_visual_id() as ffi::VisualID; - - let mut num_visuals = 0; - let vi = (xconn.xlib.XGetVisualInfo)(xconn.display, ffi::VisualIDMask, - &mut template, &mut num_visuals); - xconn.check_errors().expect("Failed to call `XGetVisualInfo`"); - assert!(!vi.is_null()); - assert!(num_visuals == 1); + let mut template: ffi::XVisualInfo = unsafe { mem::zeroed() }; + template.visualid = p.get_native_visual_id() as ffi::VisualID; + + let mut num_visuals = 0; + let vi = unsafe { + (xconn.xlib.XGetVisualInfo)( + xconn.display, + ffi::VisualIDMask, + &mut template, + &mut num_visuals, + ) + }; + xconn + .check_errors() + .expect("Failed to call `XGetVisualInfo`"); + assert!(!vi.is_null()); + assert!(num_visuals == 1); - let vi_copy = ptr::read(vi as *const _); + let vi_copy = unsafe { ptr::read(vi as *const _) }; + unsafe { (xconn.xlib.XFree)(vi as *mut _); - vi_copy } - }, + vi_copy + } }; - let window = window_builder - .with_x11_visual(&visual_infos as *const _) - .with_x11_screen(screen_id) - .build(events_loop)?; + let window = wb + .with_x11_visual(&visual_infos as *const _) + .with_x11_screen(screen_id) + .build(el)?; let xlib_window = window.get_xlib_window().unwrap(); // finish creating the OpenGL context let context = match context { - Prototype::Glx(ctxt) => { - GlContext::Glx(ctxt.finish(xlib_window)?) - }, - Prototype::Egl(ctxt) => { - GlContext::Egl(ctxt.finish(xlib_window as _)?) - }, + Prototype::Glx(ctx) => X11Context::Glx(ctx.finish(xlib_window)?), + Prototype::Egl(ctx) => { + X11Context::Egl(ctx.finish(xlib_window as _)?) + } }; // getting the root window @@ -223,11 +202,18 @@ impl Context { xconn.check_errors().expect("Failed to get root window"); // creating the color map - let colormap = unsafe { - let cmap = (xconn.xlib.XCreateColormap)(xconn.display, root, - visual_infos.visual as *mut _, - ffi::AllocNone); - xconn.check_errors().expect("Failed to call XCreateColormap"); + let colormap = { + let cmap = unsafe { + (xconn.xlib.XCreateColormap)( + xconn.display, + root, + visual_infos.visual as *mut _, + ffi::AllocNone, + ) + }; + xconn + .check_errors() + .expect("Failed to call XCreateColormap"); cmap }; @@ -240,69 +226,219 @@ impl Context { Ok((window, context)) } + #[inline] + pub fn new_separated( + window: &winit::Window, + el: &winit::EventsLoop, + pf_reqs: &PixelFormatRequirements, + gl_attr: &GlAttributes<&Context>, + ) -> Result { + let xconn = match el.get_xlib_xconnection() { + Some(xconn) => xconn, + None => { + return Err(CreationError::NoBackendAvailable(Box::new( + NoX11Connection, + ))); + } + }; + + let screen_id = window.get_xlib_screen_id().unwrap(); + + let xlib_window = window.get_xlib_window().unwrap(); + let attrs = { + let mut attrs = unsafe { ::std::mem::uninitialized() }; + unsafe { + (xconn.xlib.XGetWindowAttributes)( + xconn.display, + xlib_window, + &mut attrs, + ); + } + attrs + }; + + let visual_xid = + unsafe { (xconn.xlib.XVisualIDFromVisual)(attrs.visual) }; + let mut pf_reqs = pf_reqs.clone(); + pf_reqs.x11_visual_xid = Some(visual_xid); + pf_reqs.depth_bits = Some(attrs.depth as _); + + // start the context building process + enum Prototype<'a> { + Glx(::api::glx::ContextPrototype<'a>), + Egl(::api::egl::ContextPrototype<'a>), + } + + let builder = gl_attr.clone(); + + let builder_glx_u; + let builder_egl_u; + + let context = match gl_attr.version { + GlRequest::Latest + | GlRequest::Specific(Api::OpenGl, _) + | GlRequest::GlThenGles { .. } => { + // GLX should be preferred over EGL, otherwise crashes may occur + // on X11 – issue #314 + if let Some(_) = *GLX { + builder_glx_u = builder.map_sharing(|c| match c.context { + X11Context::Glx(ref c) => c, + _ => panic!(), + }); + Prototype::Glx(GlxContext::new( + Arc::clone(&xconn), + &pf_reqs, + &builder_glx_u, + screen_id, + // We assume they don't want transparency, as we can't + // know. + false, + )?) + } else if let Some(_) = *EGL { + builder_egl_u = builder.map_sharing(|c| match c.context { + X11Context::Egl(ref c) => c, + _ => panic!(), + }); + let native_display = egl::NativeDisplay::X11(Some( + xconn.display as *const _, + )); + Prototype::Egl(EglContext::new( + &pf_reqs, + &builder_egl_u, + native_display, + )?) + } else { + return Err(CreationError::NotSupported( + "both libglx and libEGL not present", + )); + } + } + GlRequest::Specific(Api::OpenGlEs, _) => { + if let Some(_) = *EGL { + builder_egl_u = builder.map_sharing(|c| match c.context { + X11Context::Egl(ref c) => c, + _ => panic!(), + }); + Prototype::Egl(EglContext::new( + &pf_reqs, + &builder_egl_u, + egl::NativeDisplay::X11(Some( + xconn.display as *const _, + )), + )?) + } else { + return Err(CreationError::NotSupported( + "libEGL not present", + )); + } + } + GlRequest::Specific(_, _) => { + return Err(CreationError::NotSupported( + "requested specific without gl or gles", + )); + } + }; + + // finish creating the OpenGL context + let context = match context { + Prototype::Glx(ctx) => X11Context::Glx(ctx.finish(xlib_window)?), + Prototype::Egl(ctx) => { + X11Context::Egl(ctx.finish(xlib_window as _)?) + } + }; + + // getting the root window + let root = unsafe { (xconn.xlib.XDefaultRootWindow)(xconn.display) }; + xconn.check_errors().expect("Failed to get root window"); + + // creating the color map + let colormap = { + let cmap = unsafe { + (xconn.xlib.XCreateColormap)( + xconn.display, + root, + attrs.visual as *mut _, + ffi::AllocNone, + ) + }; + xconn + .check_errors() + .expect("Failed to call XCreateColormap"); + cmap + }; + + let context = Context { + xconn: Arc::clone(&xconn), + context, + colormap, + }; + + Ok(context) + } + #[inline] pub unsafe fn make_current(&self) -> Result<(), ContextError> { match self.context { - GlContext::Glx(ref ctxt) => ctxt.make_current(), - GlContext::Egl(ref ctxt) => ctxt.make_current(), - GlContext::None => Ok(()) + X11Context::Glx(ref ctx) => ctx.make_current(), + X11Context::Egl(ref ctx) => ctx.make_current(), + X11Context::None => Ok(()), } } #[inline] pub fn is_current(&self) -> bool { match self.context { - GlContext::Glx(ref ctxt) => ctxt.is_current(), - GlContext::Egl(ref ctxt) => ctxt.is_current(), - GlContext::None => panic!() + X11Context::Glx(ref ctx) => ctx.is_current(), + X11Context::Egl(ref ctx) => ctx.is_current(), + X11Context::None => panic!(), } } #[inline] pub fn get_proc_address(&self, addr: &str) -> *const () { match self.context { - GlContext::Glx(ref ctxt) => ctxt.get_proc_address(addr), - GlContext::Egl(ref ctxt) => ctxt.get_proc_address(addr), - GlContext::None => ptr::null() + X11Context::Glx(ref ctx) => ctx.get_proc_address(addr), + X11Context::Egl(ref ctx) => ctx.get_proc_address(addr), + X11Context::None => ptr::null(), } } #[inline] pub fn swap_buffers(&self) -> Result<(), ContextError> { match self.context { - GlContext::Glx(ref ctxt) => ctxt.swap_buffers(), - GlContext::Egl(ref ctxt) => ctxt.swap_buffers(), - GlContext::None => Ok(()) + X11Context::Glx(ref ctx) => ctx.swap_buffers(), + X11Context::Egl(ref ctx) => ctx.swap_buffers(), + X11Context::None => Ok(()), } } #[inline] pub fn get_api(&self) -> Api { match self.context { - GlContext::Glx(ref ctxt) => ctxt.get_api(), - GlContext::Egl(ref ctxt) => ctxt.get_api(), - GlContext::None => panic!() + X11Context::Glx(ref ctx) => ctx.get_api(), + X11Context::Egl(ref ctx) => ctx.get_api(), + X11Context::None => panic!(), } } #[inline] pub fn get_pixel_format(&self) -> PixelFormat { match self.context { - GlContext::Glx(ref ctxt) => ctxt.get_pixel_format(), - GlContext::Egl(ref ctxt) => ctxt.get_pixel_format(), - GlContext::None => panic!() + X11Context::Glx(ref ctx) => ctx.get_pixel_format(), + X11Context::Egl(ref ctx) => ctx.get_pixel_format(), + X11Context::None => panic!(), } } #[inline] - pub unsafe fn raw_handle(&self) -> &GlContext { + pub unsafe fn raw_handle(&self) -> &X11Context { &self.context } #[inline] pub unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { match self.context { - GlContext::Egl(ref ctxt) => Some(ctxt.get_egl_display()), + X11Context::Egl(ref ctx) => Some(ctx.get_egl_display()), _ => None, } } diff --git a/src/platform/macos/helpers.rs b/src/platform/macos/helpers.rs index 7a426c67b2..011e8af980 100644 --- a/src/platform/macos/helpers.rs +++ b/src/platform/macos/helpers.rs @@ -1,12 +1,11 @@ - +use cocoa::appkit::*; +use cocoa::base::nil; use CreationError; use GlAttributes; use GlProfile; use GlRequest; use PixelFormatRequirements; use ReleaseBehavior; -use cocoa::appkit::*; -use cocoa::base::nil; pub fn get_gl_profile( opengl: &GlAttributes<&T>, @@ -15,8 +14,8 @@ pub fn get_gl_profile( let version = opengl.version.to_gl_version(); // first, compatibility profile support is strict if opengl.profile == Some(GlProfile::Compatibility) { - // Note: we are not using ranges because of a rust bug that should be fixed here: - // https://github.com/rust-lang/rust/pull/27050 + // Note: we are not using ranges because of a rust bug that should be + // fixed here: https://github.com/rust-lang/rust/pull/27050 if version.unwrap_or((2, 1)) < (3, 2) { Ok(NSOpenGLProfileVersionLegacy) } else { @@ -41,7 +40,7 @@ pub fn get_gl_profile( let mut current_idx = 0; attributes[current_idx] = NSOpenGLPFAAllowOfflineRenderers as u32; current_idx += 1; - + if let Some(true) = pf_reqs.hardware_accelerated { attributes[current_idx] = NSOpenGLPFAAccelerated as u32; current_idx += 1; @@ -51,11 +50,13 @@ pub fn get_gl_profile( attributes[current_idx] = NSOpenGLPFADoubleBuffer as u32; current_idx += 1 } - + attributes[current_idx] = NSOpenGLPFAOpenGLProfile as u32; current_idx += 1; - - for &profile in &[NSOpenGLProfileVersion4_1Core, NSOpenGLProfileVersion3_2Core] { + + for &profile in + &[NSOpenGLProfileVersion4_1Core, NSOpenGLProfileVersion3_2Core] + { attributes[current_idx] = profile as u32; let id = unsafe { NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attributes) @@ -73,7 +74,8 @@ pub fn get_gl_profile( } pub fn build_nsattributes( - pf_reqs: &PixelFormatRequirements, profile: NSOpenGLPFAOpenGLProfiles + pf_reqs: &PixelFormatRequirements, + profile: NSOpenGLPFAOpenGLProfiles, ) -> Result, CreationError> { // NOTE: OS X no longer has the concept of setting individual // color component's bit size. Instead we can only specify the @@ -84,12 +86,17 @@ pub fn build_nsattributes( let color_depth = pf_reqs.color_bits.unwrap_or(24) + alpha_depth; let mut attributes = vec![ - NSOpenGLPFAOpenGLProfile as u32, profile as u32, + NSOpenGLPFAOpenGLProfile as u32, + profile as u32, NSOpenGLPFAClosestPolicy as u32, - NSOpenGLPFAColorSize as u32, color_depth as u32, - NSOpenGLPFAAlphaSize as u32, alpha_depth as u32, - NSOpenGLPFADepthSize as u32, pf_reqs.depth_bits.unwrap_or(24) as u32, - NSOpenGLPFAStencilSize as u32, pf_reqs.stencil_bits.unwrap_or(8) as u32, + NSOpenGLPFAColorSize as u32, + color_depth as u32, + NSOpenGLPFAAlphaSize as u32, + alpha_depth as u32, + NSOpenGLPFADepthSize as u32, + pf_reqs.depth_bits.unwrap_or(24) as u32, + NSOpenGLPFAStencilSize as u32, + pf_reqs.stencil_bits.unwrap_or(8) as u32, NSOpenGLPFAAllowOfflineRenderers as u32, ]; @@ -99,7 +106,7 @@ pub fn build_nsattributes( // Note: according to Apple docs, not specifying `NSOpenGLPFADoubleBuffer` // equals to requesting a single front buffer, in which case most of the GL - // renderers will show nothing, since they draw to GL_BACK. + // renderers will show nothing, since they draw to GL_BACK. if pf_reqs.double_buffer != Some(false) { attributes.push(NSOpenGLPFADoubleBuffer as u32); } @@ -109,7 +116,7 @@ pub fn build_nsattributes( } if pf_reqs.stereoscopy { - unimplemented!(); // TODO: + unimplemented!(); // TODO: } if pf_reqs.float_color_buffer { @@ -118,8 +125,10 @@ pub fn build_nsattributes( if let Some(samples) = pf_reqs.multisampling { attributes.push(NSOpenGLPFAMultisample as u32); - attributes.push(NSOpenGLPFASampleBuffers as u32); attributes.push(1); - attributes.push(NSOpenGLPFASamples as u32); attributes.push(samples as u32); + attributes.push(NSOpenGLPFASampleBuffers as u32); + attributes.push(1); + attributes.push(NSOpenGLPFASamples as u32); + attributes.push(samples as u32); } // attribute list must be null terminated. diff --git a/src/platform/macos/mod.rs b/src/platform/macos/mod.rs index 8cdb25ae6b..edce0bcc91 100644 --- a/src/platform/macos/mod.rs +++ b/src/platform/macos/mod.rs @@ -2,27 +2,32 @@ pub use winit::MonitorId; -use CreationError; use ContextError; +use CreationError; use GlAttributes; use PixelFormat; use PixelFormatRequirements; use Robustness; -use cgl::{CGLEnable, kCGLCECrashOnRemovedFunctions, CGLSetParameter, kCGLCPSurfaceOpacity}; +use cgl::{ + kCGLCECrashOnRemovedFunctions, kCGLCPSurfaceOpacity, CGLEnable, + CGLSetParameter, +}; use cocoa::appkit::{self, NSOpenGLContext, NSOpenGLPixelFormat}; use cocoa::base::{id, nil}; use cocoa::foundation::NSAutoreleasePool; use core_foundation::base::TCFType; +use core_foundation::bundle::{ + CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName, +}; use core_foundation::string::CFString; -use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName}; use objc::runtime::{BOOL, NO}; use winit; use winit::os::macos::WindowExt; -use std::str::FromStr; use std::ops::Deref; use std::os::raw::c_void; +use std::str::FromStr; mod helpers; @@ -42,23 +47,23 @@ pub struct HeadlessContext { } impl Context { + #[inline] pub fn new( - window_builder: winit::WindowBuilder, - events_loop: &winit::EventsLoop, + wb: winit::WindowBuilder, + el: &winit::EventsLoop, pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Context>, - ) -> Result<(winit::Window, Self), CreationError> - { - let transparent = window_builder.window.transparent; - let window = window_builder.build(events_loop)?; + ) -> Result<(winit::Window, Self), CreationError> { + let transparent = wb.window.transparent; + let window = wb.build(el)?; if gl_attr.sharing.is_some() { unimplemented!() } match gl_attr.robustness { - Robustness::RobustNoResetNotification | - Robustness::RobustLoseContextOnReset => { + Robustness::RobustNoResetNotification + | Robustness::RobustLoseContextOnReset => { return Err(CreationError::RobustnessNotSupported); } _ => (), @@ -69,41 +74,59 @@ impl Context { let gl_profile = helpers::get_gl_profile(gl_attr, pf_reqs)?; let attributes = helpers::build_nsattributes(pf_reqs, gl_profile)?; unsafe { - let pixel_format = IdRef::new(NSOpenGLPixelFormat::alloc(nil) - .initWithAttributes_(&attributes)); + let pixel_format = IdRef::new( + NSOpenGLPixelFormat::alloc(nil) + .initWithAttributes_(&attributes), + ); let pixel_format = match pixel_format.non_nil() { None => return Err(CreationError::NoAvailablePixelFormat), Some(pf) => pf, }; // TODO: Add context sharing - let gl_context = IdRef::new(NSOpenGLContext::alloc(nil) - .initWithFormat_shareContext_(*pixel_format, nil)); + let gl_context = IdRef::new( + NSOpenGLContext::alloc(nil) + .initWithFormat_shareContext_(*pixel_format, nil), + ); let gl_context = match gl_context.non_nil() { Some(gl_context) => gl_context, - None => return Err(CreationError::NotSupported("could not open gl context")), + None => { + return Err(CreationError::NotSupported( + "could not open gl context", + )); + } }; let pixel_format = { - let get_attr = |attrib: appkit::NSOpenGLPixelFormatAttribute| -> i32 { - let mut value = 0; - NSOpenGLPixelFormat::getValues_forAttribute_forVirtualScreen_( + let get_attr = + |attrib: appkit::NSOpenGLPixelFormatAttribute| -> i32 { + let mut value = 0; + NSOpenGLPixelFormat::getValues_forAttribute_forVirtualScreen_( *pixel_format, &mut value, attrib, - NSOpenGLContext::currentVirtualScreen(*gl_context)); - value - }; + NSOpenGLContext::currentVirtualScreen(*gl_context), + ); + value + }; PixelFormat { - hardware_accelerated: get_attr(appkit::NSOpenGLPFAAccelerated) != 0, - color_bits: (get_attr(appkit::NSOpenGLPFAColorSize) - get_attr(appkit::NSOpenGLPFAAlphaSize)) as u8, + hardware_accelerated: get_attr( + appkit::NSOpenGLPFAAccelerated, + ) != 0, + color_bits: (get_attr(appkit::NSOpenGLPFAColorSize) + - get_attr(appkit::NSOpenGLPFAAlphaSize)) + as u8, alpha_bits: get_attr(appkit::NSOpenGLPFAAlphaSize) as u8, depth_bits: get_attr(appkit::NSOpenGLPFADepthSize) as u8, - stencil_bits: get_attr(appkit::NSOpenGLPFAStencilSize) as u8, + stencil_bits: get_attr(appkit::NSOpenGLPFAStencilSize) + as u8, stereoscopy: get_attr(appkit::NSOpenGLPFAStereo) != 0, - double_buffer: get_attr(appkit::NSOpenGLPFADoubleBuffer) != 0, - multisampling: if get_attr(appkit::NSOpenGLPFAMultisample) > 0 { + double_buffer: get_attr(appkit::NSOpenGLPFADoubleBuffer) + != 0, + multisampling: if get_attr(appkit::NSOpenGLPFAMultisample) + > 0 + { Some(get_attr(appkit::NSOpenGLPFASamples) as u16) } else { None @@ -114,16 +137,29 @@ impl Context { gl_context.setView_(view); let value = if gl_attr.vsync { 1 } else { 0 }; - gl_context.setValues_forParameter_(&value, appkit::NSOpenGLContextParameter::NSOpenGLCPSwapInterval); + gl_context.setValues_forParameter_( + &value, + appkit::NSOpenGLContextParameter::NSOpenGLCPSwapInterval, + ); if transparent { let mut opacity = 0; - CGLSetParameter(gl_context.CGLContextObj() as *mut _, kCGLCPSurfaceOpacity, &mut opacity); + CGLSetParameter( + gl_context.CGLContextObj() as *mut _, + kCGLCPSurfaceOpacity, + &mut opacity, + ); } - CGLEnable(gl_context.CGLContextObj() as *mut _, kCGLCECrashOnRemovedFunctions); + CGLEnable( + gl_context.CGLContextObj() as *mut _, + kCGLCECrashOnRemovedFunctions, + ); - let context = WindowedContext { context: gl_context, pixel_format: pixel_format }; + let context = WindowedContext { + context: gl_context, + pixel_format: pixel_format, + }; Ok((window, Context::WindowedContext(context))) } } @@ -133,32 +169,44 @@ impl Context { _el: &winit::EventsLoop, pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Context>, - shareable_with_windowed_contexts: bool, ) -> Result { - assert!(!shareable_with_windowed_contexts); // TODO: Implement if possible - let gl_profile = helpers::get_gl_profile(gl_attr, pf_reqs)?; let attributes = helpers::build_nsattributes(pf_reqs, gl_profile)?; let context = unsafe { - let pixelformat = NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attributes); + let pixelformat = NSOpenGLPixelFormat::alloc(nil) + .initWithAttributes_(&attributes); if pixelformat == nil { - return Err(CreationError::OsError(format!("Could not create the pixel format"))); + return Err(CreationError::OsError(format!( + "Could not create the pixel format" + ))); } - let context = NSOpenGLContext::alloc(nil).initWithFormat_shareContext_(pixelformat, nil); + let context = NSOpenGLContext::alloc(nil) + .initWithFormat_shareContext_(pixelformat, nil); if context == nil { - return Err(CreationError::OsError(format!("Could not create the rendering context"))); + return Err(CreationError::OsError(format!( + "Could not create the rendering context" + ))); } IdRef::new(context) }; - let headless = HeadlessContext { - context, - }; + let headless = HeadlessContext { context }; Ok(Context::HeadlessContext(headless)) } + /// See the docs in the crate root file. + #[inline] + pub fn new_separated( + _window: &winit::Window, + _el: &winit::EventsLoop, + _pf_reqs: &PixelFormatRequirements, + _gl_attr: &GlAttributes<&Context>, + ) -> Result { + unimplemented!() + } + pub fn resize(&self, _width: u32, _height: u32) { match *self { Context::WindowedContext(ref c) => unsafe { c.context.update() }, @@ -185,8 +233,8 @@ impl Context { pub fn is_current(&self) -> bool { unsafe { let context = match *self { - Context::WindowedContext(ref c) => &c.context, - Context::HeadlessContext(ref c) => &c.context, + Context::WindowedContext(ref c) => *c.context, + Context::HeadlessContext(ref c) => *c.context, }; let pool = NSAutoreleasePool::new(nil); @@ -204,11 +252,18 @@ impl Context { pub fn get_proc_address(&self, addr: &str) -> *const () { let symbol_name: CFString = FromStr::from_str(addr).unwrap(); - let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap(); - let framework = - unsafe { CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef()) }; + let framework_name: CFString = + FromStr::from_str("com.apple.opengl").unwrap(); + let framework = unsafe { + CFBundleGetBundleWithIdentifier( + framework_name.as_concrete_TypeRef(), + ) + }; let symbol = unsafe { - CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef()) + CFBundleGetFunctionPointerForName( + framework, + symbol_name.as_concrete_TypeRef(), + ) }; symbol as *const _ } @@ -266,7 +321,11 @@ impl IdRef { } fn non_nil(self) -> Option { - if self.0 == nil { None } else { Some(self) } + if self.0 == nil { + None + } else { + Some(self) + } } } diff --git a/src/platform/mod.rs b/src/platform/mod.rs index f920b989ef..f720b5a316 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -1,26 +1,40 @@ pub use self::platform::*; #[cfg(target_os = "windows")] -#[path="windows/mod.rs"] +#[path = "windows/mod.rs"] mod platform; -#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))] -#[path="linux/mod.rs"] +#[cfg(any( + target_os = "linux", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "netbsd", + target_os = "openbsd" +))] +#[path = "linux/mod.rs"] mod platform; #[cfg(target_os = "macos")] -#[path="macos/mod.rs"] +#[path = "macos/mod.rs"] mod platform; #[cfg(target_os = "android")] -#[path="android/mod.rs"] +#[path = "android/mod.rs"] mod platform; #[cfg(target_os = "ios")] -#[path="ios/mod.rs"] +#[path = "ios/mod.rs"] mod platform; #[cfg(target_os = "emscripten")] -#[path="emscripten/mod.rs"] +#[path = "emscripten/mod.rs"] mod platform; -#[cfg(all(not(target_os = "ios"), not(target_os = "windows"), not(target_os = "linux"), - not(target_os = "macos"), not(target_os = "android"), not(target_os = "dragonfly"), - not(target_os = "freebsd"), not(target_os = "netbsd"), not(target_os = "openbsd"), - not(target_os = "emscripten")))] +#[cfg(all( + not(target_os = "ios"), + not(target_os = "windows"), + not(target_os = "linux"), + not(target_os = "macos"), + not(target_os = "android"), + not(target_os = "dragonfly"), + not(target_os = "freebsd"), + not(target_os = "netbsd"), + not(target_os = "openbsd"), + not(target_os = "emscripten") +))] use this_platform_is_not_supported; diff --git a/src/platform/windows/context.rs b/src/platform/windows/context.rs deleted file mode 100644 index 3e98898ea4..0000000000 --- a/src/platform/windows/context.rs +++ /dev/null @@ -1,201 +0,0 @@ -#![cfg(target_os = "windows")] - -use std::os::raw; -use std::ptr; - -use winapi::shared::windef::HWND; -use winit; - -use Api; -use ContextError; -use CreationError; -use GlAttributes; -use GlRequest; -use PixelFormat; -use PixelFormatRequirements; - -use api::egl; -use api::egl::ffi::egl::Egl; -use api::egl::Context as EglContext; -use api::wgl::Context as WglContext; -use os::windows::WindowExt; -use platform::RawHandle; - -unsafe impl Send for Context {} -unsafe impl Sync for Context {} - -pub enum Context { - /// A regular window - Egl(EglContext), - Wgl(WglContext), - /// A regular window, but invisible. - HiddenWindowEgl(winit::Window, EglContext), - HiddenWindowWgl(winit::Window, WglContext), - /// An EGL pbuffer. - EglPbuffer(EglContext), -} - -impl Context { - /// See the docs in the crate root file. - pub unsafe fn new( - window_builder: winit::WindowBuilder, - events_loop: &winit::EventsLoop, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Self>, - egl: Option<&Egl>, - ) -> Result<(winit::Window, Self), CreationError> { - let window = window_builder.build(events_loop)?; - let gl_attr = gl_attr.clone().map_sharing(|ctxt| { - match *ctxt { - Context::Wgl(ref c) => c.get_hglrc(), - // FIXME - Context::HiddenWindowWgl(_, _) => unimplemented!(), - Context::Egl(_) | Context::EglPbuffer(_) | Context::HiddenWindowEgl(_, _) => { - unimplemented!() - } - } - }); - let context_result = { - let w = window.get_hwnd() as HWND; - match gl_attr.version { - GlRequest::Specific(Api::OpenGlEs, (_major, _minor)) => { - if let Some(egl) = egl { - if let Ok(c) = EglContext::new( - egl.clone(), - &pf_reqs, - &gl_attr.clone().map_sharing(|_| unimplemented!()), - egl::NativeDisplay::Other(Some(ptr::null())), - ).and_then(|p| p.finish(w)) - { - Ok(Context::Egl(c)) - } else { - WglContext::new(&pf_reqs, &gl_attr, w).map(Context::Wgl) - } - } else { - // falling back to WGL, which is always available - WglContext::new(&pf_reqs, &gl_attr, w).map(Context::Wgl) - } - } - _ => WglContext::new(&pf_reqs, &gl_attr, w).map(Context::Wgl), - } - }; - context_result.map(|context| (window, context)) - } - - #[inline] - pub unsafe fn new_context( - el: &winit::EventsLoop, - pf_reqs: &PixelFormatRequirements, - gl_attr: &GlAttributes<&Context>, - shareable_with_windowed_contexts: bool, - egl: Option<&Egl>, - ) -> Result { - assert!(!shareable_with_windowed_contexts); // TODO: Implement if possible - - // if EGL is available, we try using EGL first - // if EGL returns an error, we try the hidden window method - if let Some(egl) = egl { - let gl_attr = &gl_attr.clone().map_sharing(|_| unimplemented!()); // TODO - let native_display = egl::NativeDisplay::Other(None); - let context = EglContext::new(egl.clone(), pf_reqs, &gl_attr, native_display) - .and_then(|prototype| prototype.finish_pbuffer((1, 1))) - .map(|ctxt| Context::EglPbuffer(ctxt)); - if let Ok(context) = context { - return Ok(context); - } - } - let window_builder = winit::WindowBuilder::new().with_visibility(false); - let gl_attr = &gl_attr.clone().map_sharing(|_| unimplemented!()); // TODO - Self::new(window_builder, &el, pf_reqs, gl_attr, egl).map(|(window, context)| match context - { - Context::Egl(context) => Context::HiddenWindowEgl(window, context), - Context::Wgl(context) => Context::HiddenWindowWgl(window, context), - _ => unreachable!(), - }) - } - - #[inline] - pub fn resize(&self, _width: u32, _height: u32) { - // Method is for API consistency. - } - - #[inline] - pub unsafe fn make_current(&self) -> Result<(), ContextError> { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => c.make_current(), - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => c.make_current(), - } - } - - #[inline] - pub fn is_current(&self) -> bool { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => c.is_current(), - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => c.is_current(), - } - } - - #[inline] - pub fn get_proc_address(&self, addr: &str) -> *const () { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => c.get_proc_address(addr), - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => c.get_proc_address(addr), - } - } - - #[inline] - pub fn swap_buffers(&self) -> Result<(), ContextError> { - match *self { - Context::Wgl(ref c) => c.swap_buffers(), - Context::Egl(ref c) => c.swap_buffers(), - _ => unreachable!(), - } - } - - #[inline] - pub fn get_api(&self) -> Api { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => c.get_api(), - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => c.get_api(), - } - } - - #[inline] - pub fn get_pixel_format(&self) -> PixelFormat { - match *self { - Context::Wgl(ref c) => c.get_pixel_format(), - Context::Egl(ref c) => c.get_pixel_format(), - _ => unreachable!(), - } - } - - #[inline] - pub unsafe fn raw_handle(&self) -> RawHandle { - match *self { - Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => { - RawHandle::Wgl(c.get_hglrc()) - } - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => RawHandle::Egl(c.raw_handle()), - } - } - - #[inline] - pub unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { - match *self { - Context::Egl(ref c) - | Context::HiddenWindowEgl(_, ref c) - | Context::EglPbuffer(ref c) => Some(c.get_egl_display()), - _ => None, - } - } -} diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs index 096cfdf949..9876ee1c1a 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform/windows/mod.rs @@ -1,20 +1,24 @@ #![cfg(target_os = "windows")] -use std::ffi::CString; -use std::ops::{Deref, DerefMut}; +use std::os::raw; +use std::ptr; -use winapi::shared::windef::HGLRC; -use winapi::um::libloaderapi::*; +use winapi::shared::windef::{HGLRC, HWND}; use winit; +use Api; +use ContextError; use CreationError; use GlAttributes; +use GlRequest; +use PixelFormat; use PixelFormatRequirements; use api::egl; -use api::egl::ffi::egl::Egl; - -mod context; +use api::egl::Context as EglContext; +use api::egl::EGL; +use api::wgl::Context as WglContext; +use os::windows::WindowExt; /// Context handles available on Windows. #[derive(Clone, Debug)] @@ -23,89 +27,256 @@ pub enum RawHandle { Wgl(HGLRC), } -/// Stupid wrapper because `*const libc::c_void` doesn't implement `Sync`. -struct EglWrapper(Egl); -unsafe impl Sync for EglWrapper {} - -lazy_static! { - // An EGL implementation available on the system. - static ref EGL: Option = { - // the ATI drivers provide an EGL implementation in their DLLs - let ati_dll_name = if cfg!(target_pointer_width = "64") { - b"atio6axx.dll\0" - } else { - b"atioglxx.dll\0" - }; - - for dll_name in &[b"libEGL.dll\0" as &[u8], ati_dll_name] { - let dll = unsafe { LoadLibraryA(dll_name.as_ptr() as *const _) }; - if dll.is_null() { - continue; - } - - let egl = Egl::load_with(|name| { - let name = CString::new(name).unwrap(); - unsafe { GetProcAddress(dll, name.as_ptr()) as *const _ } - }); - - return Some(EglWrapper(egl)) - } - - None - }; +pub enum Context { + /// A regular window + Egl(EglContext), + Wgl(WglContext), + /// A regular window, but invisible. + HiddenWindowEgl(winit::Window, EglContext), + HiddenWindowWgl(winit::Window, WglContext), + /// An EGL pbuffer. + EglPbuffer(EglContext), } -/// The Win32 implementation of the main `Context` object. -pub struct Context(context::Context); +unsafe impl Send for Context {} +unsafe impl Sync for Context {} impl Context { /// See the docs in the crate root file. #[inline] - pub unsafe fn new( - window_builder: winit::WindowBuilder, - events_loop: &winit::EventsLoop, + pub fn new( + wb: winit::WindowBuilder, + el: &winit::EventsLoop, pf_reqs: &PixelFormatRequirements, - opengl: &GlAttributes<&Self>, + gl_attr: &GlAttributes<&Self>, ) -> Result<(winit::Window, Self), CreationError> { - context::Context::new( - window_builder, - events_loop, - pf_reqs, - &opengl.clone().map_sharing(|w| &w.0), - EGL.as_ref().map(|w| &w.0), - ).map(|(w, c)| (w, Context(c))) + let window = wb.build(el)?; + let ctx = Self::new_separated(&window, el, pf_reqs, gl_attr)?; + + Ok((window, ctx)) } - /// See the docs in the crate root file. #[inline] - pub unsafe fn new_context( - events_loop: &winit::EventsLoop, + pub fn new_separated( + window: &winit::Window, + _el: &winit::EventsLoop, pf_reqs: &PixelFormatRequirements, gl_attr: &GlAttributes<&Self>, - shareable_with_windowed_contexts: bool, ) -> Result { - context::Context::new_context( - events_loop, - pf_reqs, - &gl_attr.clone().map_sharing(|w| &w.0), - shareable_with_windowed_contexts, - EGL.as_ref().map(|w| &w.0), - ).map(|c| Context(c)) + let w = window.get_hwnd() as HWND; + match gl_attr.version { + GlRequest::Specific(Api::OpenGlEs, (_major, _minor)) => { + match (gl_attr.sharing, &*EGL) { + // We must use WGL. + (Some(&Context::HiddenWindowWgl(_, _)), _) + | (Some(&Context::Wgl(_)), _) + | (None, None) => { + let gl_attr_wgl = + gl_attr.clone().map_sharing(|ctx| match *ctx { + Context::HiddenWindowWgl(_, ref c) + | Context::Wgl(ref c) => c.get_hglrc(), + _ => unreachable!(), + }); + unsafe { + WglContext::new(&pf_reqs, &gl_attr_wgl, w) + .map(Context::Wgl) + } + } + // We must use EGL. + (Some(_), Some(_)) => { + let gl_attr_egl = + gl_attr.clone().map_sharing(|ctx| match *ctx { + Context::Egl(ref c) + | Context::EglPbuffer(ref c) + | Context::HiddenWindowEgl(_, ref c) => c, + _ => unreachable!(), + }); + + EglContext::new( + &pf_reqs, + &gl_attr_egl, + egl::NativeDisplay::Other(Some(ptr::null())), + ) + .and_then(|p| p.finish(w)) + .map(|c| Context::Egl(c)) + } + // Try EGL, fallback to WGL. + (None, Some(_)) => { + let gl_attr_egl = + gl_attr.clone().map_sharing(|_| unreachable!()); + let gl_attr_wgl = + gl_attr.clone().map_sharing(|_| unreachable!()); + + if let Ok(c) = EglContext::new( + &pf_reqs, + &gl_attr_egl, + egl::NativeDisplay::Other(Some(ptr::null())), + ) + .and_then(|p| p.finish(w)) + { + Ok(Context::Egl(c)) + } else { + unsafe { + WglContext::new(&pf_reqs, &gl_attr_wgl, w) + .map(Context::Wgl) + } + } + } + _ => panic!(), + } + } + _ => { + let gl_attr_wgl = + gl_attr.clone().map_sharing(|ctx| match *ctx { + Context::HiddenWindowWgl(_, ref c) + | Context::Wgl(ref c) => c.get_hglrc(), + _ => panic!(), + }); + unsafe { + WglContext::new(&pf_reqs, &gl_attr_wgl, w).map(Context::Wgl) + } + } + } } -} -impl Deref for Context { - type Target = context::Context; + #[inline] + pub fn new_context( + el: &winit::EventsLoop, + pf_reqs: &PixelFormatRequirements, + gl_attr: &GlAttributes<&Context>, + ) -> Result { + // if EGL is available, we try using EGL first + // if EGL returns an error, we try the hidden window method + match (gl_attr.sharing, &*EGL) { + (None, Some(_)) + | (Some(&Context::Egl(_)), Some(_)) + | (Some(&Context::HiddenWindowEgl(_, _)), Some(_)) + | (Some(&Context::EglPbuffer(_)), Some(_)) => { + let gl_attr_egl = + gl_attr.clone().map_sharing(|ctx| match *ctx { + Context::Egl(ref c) + | Context::EglPbuffer(ref c) + | Context::HiddenWindowEgl(_, ref c) => c, + _ => unreachable!(), + }); + + let native_display = egl::NativeDisplay::Other(None); + let context = + EglContext::new(pf_reqs, &gl_attr_egl, native_display) + .and_then(|prototype| prototype.finish_pbuffer((1, 1))) + .map(|ctx| Context::EglPbuffer(ctx)); + + if let Ok(context) = context { + return Ok(context); + } + } + _ => (), + } + + let wb = winit::WindowBuilder::new().with_visibility(false); + Self::new(wb, &el, pf_reqs, gl_attr).map(|(window, context)| { + match context { + Context::Egl(context) => { + Context::HiddenWindowEgl(window, context) + } + Context::Wgl(context) => { + Context::HiddenWindowWgl(window, context) + } + _ => unreachable!(), + } + }) + } #[inline] - fn deref(&self) -> &context::Context { - &self.0 + pub fn resize(&self, _width: u32, _height: u32) { + // Method is for API consistency. + } + + #[inline] + pub unsafe fn make_current(&self) -> Result<(), ContextError> { + match *self { + Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => { + c.make_current() + } + Context::Egl(ref c) + | Context::HiddenWindowEgl(_, ref c) + | Context::EglPbuffer(ref c) => c.make_current(), + } + } + + #[inline] + pub fn is_current(&self) -> bool { + match *self { + Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => { + c.is_current() + } + Context::Egl(ref c) + | Context::HiddenWindowEgl(_, ref c) + | Context::EglPbuffer(ref c) => c.is_current(), + } + } + + #[inline] + pub fn get_proc_address(&self, addr: &str) -> *const () { + match *self { + Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => { + c.get_proc_address(addr) + } + Context::Egl(ref c) + | Context::HiddenWindowEgl(_, ref c) + | Context::EglPbuffer(ref c) => c.get_proc_address(addr), + } + } + + #[inline] + pub fn swap_buffers(&self) -> Result<(), ContextError> { + match *self { + Context::Wgl(ref c) => c.swap_buffers(), + Context::Egl(ref c) => c.swap_buffers(), + _ => unreachable!(), + } } -} -impl DerefMut for Context { #[inline] - fn deref_mut(&mut self) -> &mut context::Context { - &mut self.0 + pub fn get_api(&self) -> Api { + match *self { + Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => { + c.get_api() + } + Context::Egl(ref c) + | Context::HiddenWindowEgl(_, ref c) + | Context::EglPbuffer(ref c) => c.get_api(), + } + } + + #[inline] + pub fn get_pixel_format(&self) -> PixelFormat { + match *self { + Context::Wgl(ref c) => c.get_pixel_format(), + Context::Egl(ref c) => c.get_pixel_format(), + _ => unreachable!(), + } + } + + #[inline] + pub unsafe fn raw_handle(&self) -> RawHandle { + match *self { + Context::Wgl(ref c) | Context::HiddenWindowWgl(_, ref c) => { + RawHandle::Wgl(c.get_hglrc()) + } + Context::Egl(ref c) + | Context::HiddenWindowEgl(_, ref c) + | Context::EglPbuffer(ref c) => RawHandle::Egl(c.raw_handle()), + } + } + + #[inline] + pub unsafe fn get_egl_display(&self) -> Option<*const raw::c_void> { + match *self { + Context::Egl(ref c) + | Context::HiddenWindowEgl(_, ref c) + | Context::EglPbuffer(ref c) => Some(c.get_egl_display()), + _ => None, + } } } diff --git a/src/separated.rs b/src/separated.rs new file mode 100644 index 0000000000..99230d16a9 --- /dev/null +++ b/src/separated.rs @@ -0,0 +1,126 @@ +use super::*; + +/// Represents an OpenGL context which has been associated with a preexisting +/// window. +/// +/// # Example +/// +/// ```no_run +/// # extern crate glutin; +/// # use glutin::ContextTrait; +/// # fn main() { +/// let mut el = glutin::EventsLoop::new(); +/// let win = glutin::WindowBuilder::new().build(&el).unwrap(); +/// let separated_context = glutin::ContextBuilder::new() +/// .build_separated(&win, &el) +/// .unwrap(); +/// +/// unsafe { separated_context.make_current().unwrap() }; +/// +/// loop { +/// el.poll_events(|event| { +/// match event { +/// // process events here +/// _ => (), +/// } +/// }); +/// +/// // draw everything here +/// +/// separated_context.swap_buffers(); +/// std::thread::sleep(std::time::Duration::from_millis(17)); +/// } +/// # } +/// ``` +pub struct SeparatedContext { + context: Context, +} + +impl SeparatedContext { + /// Builds the GL context using the passed `Window`, returning the context + /// as a `SeparatedContext`. + /// + /// One notable limitation of the Wayland backend when it comes to shared + /// contexts is that both contexts must use the same events loop. + /// + /// Errors can occur in two scenarios: + /// - If the window could not be created (via permission denied, + /// incompatible system, out of memory, etc.). This should be very rare. + /// - If the OpenGL context could not be created. This generally happens + /// because the underlying platform doesn't support a requested feature. + pub fn new( + window: &Window, + cb: ContextBuilder, + el: &EventsLoop, + ) -> Result { + let ContextBuilder { pf_reqs, gl_attr } = cb; + let gl_attr = gl_attr.map_sharing(|ctx| &ctx.context); + + platform::Context::new_separated(window, el, &pf_reqs, &gl_attr).map( + |context| SeparatedContext { + context: Context { context }, + }, + ) + } + + /// Borrow the inner GL `Context`. + pub fn context(&self) -> &Context { + &self.context + } + + /// 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. + /// + /// **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. + pub fn swap_buffers(&self) -> Result<(), ContextError> { + self.context.context.swap_buffers() + } + + /// Returns the pixel format of the main framebuffer of the context. + pub fn get_pixel_format(&self) -> PixelFormat { + self.context.context.get_pixel_format() + } + + /// Resize the context. + /// + /// Some platforms (macOS, Wayland) require being manually updated when + /// their window or surface is resized. + /// + /// The easiest way of doing this is to take every `Resized` window event + /// that is received with a `LogicalSize` and convert it to a + /// `PhysicalSize` and pass it into this function. + pub fn resize(&self, size: dpi::PhysicalSize) { + let (width, height) = size.into(); + self.context.context.resize(width, height); + } +} + +impl ContextTrait for SeparatedContext { + unsafe fn make_current(&self) -> Result<(), ContextError> { + self.context.make_current() + } + + fn is_current(&self) -> bool { + self.context.is_current() + } + + fn get_proc_address(&self, addr: &str) -> *const () { + self.context.get_proc_address(addr) + } + + fn get_api(&self) -> Api { + self.context.get_api() + } +} + +impl std::ops::Deref for SeparatedContext { + type Target = Context; + fn deref(&self) -> &Self::Target { + &self.context + } +}