From 0756feb62f1fb9d9d886cf179d1230e5bb93be9b Mon Sep 17 00:00:00 2001 From: Hal Gentz Date: Mon, 25 Feb 2019 16:53:32 -0700 Subject: [PATCH] Gentz' misc stuff, containing the following: Adds context sharing support for the X11 and Wayland backends Separated context support for both Wayland and X11. Add sharing support to windows. Added CHANGELOG entry and fix doc typos Merged @osspial's context_api_polish changes Marked unsafe functions that should be safe as safe. Refactored code to be more consistent/cleaner. Ran rustfmt on everything. Removed `shareable_with_windowed_contexts` and separate ext for OSMesa. Merged @yyt's macos fix. Renamed `GlWindow` to `CombinedContext`. We no longer load `libegl.so` and `libgl.so` multiple times. Renamed `GlContext{,Ext}` to `ContextTrait{,Ext}`. Signed-off-by: Hal Gentz --- CHANGELOG.md | 15 + Cargo.toml | 10 +- README.md | 25 +- build.rs | 255 +++++---- examples/fullscreen.rs | 52 +- examples/multiwindow.rs | 52 +- examples/separated_context.rs | 57 ++ examples/sharing.rs | 83 +-- examples/support/mod.rs | 68 ++- examples/transparent.rs | 44 +- examples/window.rs | 33 +- rustfmt.toml | 3 + src/api/android/ffi.rs | 108 ++-- src/api/android/mod.rs | 60 ++- src/api/caca/mod.rs | 80 ++- src/api/dlloader/mod.rs | 65 +++ src/api/dlopen.rs | 15 - src/api/egl/ffi.rs | 9 +- src/api/egl/mod.rs | 845 ++++++++++++++++++++---------- src/api/glx/mod.rs | 512 ++++++++++++------ src/api/ios/ffi.rs | 6 +- src/api/ios/mod.rs | 210 +++++--- src/api/mod.rs | 4 +- src/api/osmesa/mod.rs | 89 ++-- src/api/wgl/gl.rs | 2 +- src/api/wgl/make_current_guard.rs | 25 +- src/api/wgl/mod.rs | 550 ++++++++++++------- src/combined.rs | 131 +++++ src/context.rs | 67 +++ src/lib.rs | 524 +++++++----------- src/os/android.rs | 4 +- src/os/ios.rs | 2 +- src/os/macos.rs | 4 +- src/os/mod.rs | 8 +- src/os/unix.rs | 16 +- src/os/windows.rs | 8 +- src/platform/emscripten/ffi.rs | 112 ++-- src/platform/emscripten/mod.rs | 54 +- src/platform/linux/mod.rs | 282 ++++++---- src/platform/linux/wayland.rs | 69 ++- src/platform/linux/x11.rs | 430 +++++++++------ src/platform/macos/helpers.rs | 47 +- src/platform/macos/mod.rs | 157 ++++-- src/platform/mod.rs | 36 +- src/platform/windows/context.rs | 201 ------- src/platform/windows/mod.rs | 311 ++++++++--- src/separated.rs | 126 +++++ 47 files changed, 3759 insertions(+), 2107 deletions(-) create mode 100644 examples/separated_context.rs create mode 100644 rustfmt.toml create mode 100644 src/api/dlloader/mod.rs delete mode 100644 src/api/dlopen.rs create mode 100644 src/combined.rs create mode 100644 src/context.rs delete mode 100644 src/platform/windows/context.rs create mode 100644 src/separated.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 8694a7fb3e..869acb26a6 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 11c6fa5ea9..1d2074c503 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 + } +}