From b950714c0302319db10595fc9147f0a28b317839 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Thu, 18 May 2017 22:28:02 +0200 Subject: [PATCH 01/13] First EGL Rework draft --- Cargo.toml | 13 +- build.rs | 30 ++ src/backend/glium.rs | 14 +- src/backend/glutin.rs | 2 +- src/backend/graphics/egl.rs | 662 +++++++++++++++++++++++++++++++++ src/backend/graphics/mod.rs | 2 +- src/backend/graphics/opengl.rs | 96 ----- src/backend/mod.rs | 9 +- src/backend/winit.rs | 655 ++++++++++++++++++++++++++++++++ src/lib.rs | 8 +- 10 files changed, 1375 insertions(+), 116 deletions(-) create mode 100644 build.rs create mode 100644 src/backend/graphics/egl.rs delete mode 100644 src/backend/graphics/opengl.rs create mode 100644 src/backend/winit.rs diff --git a/Cargo.toml b/Cargo.toml index 3527288..49f4eac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,22 +5,27 @@ authors = ["Victor Berger "] license = "MIT" [dependencies] -wayland-server = "0.9.1" +wayland-server = "0.9.4" nix = "0.7.0" xkbcommon = "0.2.1" tempfile = "2.1.5" slog = { version = "2.0.0" } slog-stdlog = "2.0.0-0.2" -glutin = { version = "~0.7.4", optional = true } +libloading = "0.4.0" +wayland-client = { version = "~0.8.6", optional = true } +winit = { version = "~0.6.4", optional = true } glium = { version = "~0.16.0", optional = true } input = { version = "~0.1.1", optional = true } clippy = { version = "*", optional = true } +[build-dependencies] +gl_generator = "0.5" + [dev-dependencies] slog-term = "~1.5" [features] -default = ["backend_glutin", "backend_libinput", "renderer_glium"] -backend_glutin = ["glutin", "wayland-server/dlopen"] +default = ["backend_winit", "backend_libinput", "renderer_glium"] +backend_winit = ["winit", "wayland-server/dlopen", "wayland-client", "wayland-client/dlopen"] renderer_glium = ["glium"] backend_libinput = ["input"] diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..d846dc8 --- /dev/null +++ b/build.rs @@ -0,0 +1,30 @@ +extern crate gl_generator; + +use gl_generator::{Registry, Api, Profile, Fallbacks}; + +use std::env; +use std::fs::File; +use std::path::PathBuf; + +fn main() { + let dest = PathBuf::from(&env::var("OUT_DIR").unwrap()); + + println!("cargo:rerun-if-changed=build.rs"); + + 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(); +} diff --git a/src/backend/glium.rs b/src/backend/glium.rs index 7814443..26eefd9 100644 --- a/src/backend/glium.rs +++ b/src/backend/glium.rs @@ -1,6 +1,4 @@ - - -use backend::graphics::opengl::{OpenglGraphicsBackend, SwapBuffersError}; +use backend::graphics::egl::{EGLGraphicsBackend, SwapBuffersError}; use glium::SwapBuffersError as GliumSwapBuffersError; use glium::backend::Backend; @@ -15,19 +13,19 @@ impl From for GliumSwapBuffersError { } } -pub struct GliumGraphicBackend(T); +pub struct GliumGraphicBackend(T); -pub trait IntoGlium: OpenglGraphicsBackend + Sized { +pub trait IntoGlium: EGLGraphicsBackend + Sized { fn into_glium(self) -> GliumGraphicBackend; } -impl IntoGlium for T { +impl IntoGlium for T { fn into_glium(self) -> GliumGraphicBackend { GliumGraphicBackend(self) } } -unsafe impl Backend for GliumGraphicBackend { +unsafe impl Backend for GliumGraphicBackend { fn swap_buffers(&self) -> Result<(), GliumSwapBuffersError> { self.0.swap_buffers().map_err(Into::into) } @@ -45,6 +43,6 @@ unsafe impl Backend for GliumGraphicBackend { } unsafe fn make_current(&self) { - self.0.make_current() + self.0.make_current().expect("Context was lost") } } diff --git a/src/backend/glutin.rs b/src/backend/glutin.rs index d09401a..fe1ba84 100644 --- a/src/backend/glutin.rs +++ b/src/backend/glutin.rs @@ -21,7 +21,7 @@ use std::rc::Rc; /// Create a new `GlutinHeadlessRenderer` which implements the `OpenglRenderer` graphics /// backend trait pub fn init_headless_renderer() -> Result { - init_headless_renderer_from_builder(HeadlessRendererBuilder::new(1024, 600)) + init_headless_renderer_from_builder(HeadlessRendererBuilder::new(1024, 600) } /// Create a new `GlutinHeadlessRenderer`, which implements the `OpenglRenderer` graphics diff --git a/src/backend/graphics/egl.rs b/src/backend/graphics/egl.rs new file mode 100644 index 0000000..694a1d0 --- /dev/null +++ b/src/backend/graphics/egl.rs @@ -0,0 +1,662 @@ +//! Common traits and types for opengl rendering on graphics backends + +/// Large parts of the following file are taken from +/// https://github.com/tomaka/glutin/tree/master/src/api/egl at commit +/// `044e651edf67a2029eecc650dd42546af1501414` +/// +/// It therefor falls under glutin's Apache 2.0 license +/// (see https://github.com/tomaka/glutin/blob/master/LICENSE) + +use super::GraphicsBackend; + +use libloading::Library; +use nix::{c_int, c_void}; + +use std::ffi::{CStr, CString}; +use std::error::{self, Error}; +use std::fmt; +use std::io; +use std::ptr; +use std::mem; + +mod ffi { + use nix::c_void; + use nix::libc::{uint64_t, int32_t, c_long}; + + pub type khronos_utime_nanoseconds_t = khronos_uint64_t; + pub type khronos_uint64_t = uint64_t; + pub type khronos_ssize_t = c_long; + pub type EGLint = int32_t; + pub type EGLNativeDisplayType = NativeDisplayType; + pub type EGLNativePixmapType = NativePixmapType; + pub type EGLNativeWindowType = NativeWindowType; + pub type NativeDisplayType = *const c_void; + pub type NativePixmapType = *const c_void; + pub type NativeWindowType = *const c_void; + + pub mod egl { + use super::*; + + include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs")); + } +} + +#[derive(Clone, Copy)] +pub enum Native { + X11(ffi::NativeDisplayType, ffi::NativeWindowType), + Wayland(ffi::NativeDisplayType, ffi::NativeWindowType), + Gbm(ffi::NativeDisplayType, ffi::NativeWindowType), +} + +#[derive(Debug)] +pub enum CreationError { + IoError(io::Error), + OsError(String), + RobustnessNotSupported, + OpenGlVersionNotSupported, + NoAvailablePixelFormat, + NotSupported, +} + +impl From for CreationError { + fn from(err: io::Error) -> Self { + CreationError::IoError(err) + } +} + +impl fmt::Display for CreationError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { + formatter.write_str(self.description())?; + if let Some(err) = error::Error::cause(self) { + write!(formatter, ": {}", err)?; + } + Ok(()) + } +} + +impl error::Error for CreationError { + fn description(&self) -> &str { + match *self { + CreationError::IoError(ref err) => err.description(), + CreationError::OsError(ref text) => &text, + 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 criterias.", + CreationError::NotSupported => "Context creation is not supported on the current window system", + } + } + + fn cause(&self) -> Option<&error::Error> { + match *self { + CreationError::IoError(ref err) => Some(err), + _ => None + } + } +} + +pub struct EGLContext { + context: *const c_void, + display: *const c_void, + egl: ffi::egl::Egl, + surface: *const c_void, + pixel_format: PixelFormat, +} + +impl EGLContext { + pub unsafe fn new(native: Native, mut attributes: GlAttributes, reqs: PixelFormatRequirements) -> Result { + let lib = Library::new("libEGL.so.1")?; + let egl = ffi::egl::Egl::load_with(|sym| { + let sym = CString::new(sym).unwrap(); + unsafe { &*lib.get(sym.as_bytes()).unwrap() as *const _ } + }); + + // If no version is given, try OpenGLES 3.0, if available, + // fallback to 2.0 otherwise + let version = match attributes.version { + Some((3, x)) => (3, x), + Some((2, x)) => (2, x), + None => { + attributes.version = Some((3,0)); + match EGLContext::new(native, attributes, reqs) { + Ok(x) => return Ok(x), + Err(_) => { + //TODO log + attributes.version = Some((2,0)); + return EGLContext::new(native, attributes, reqs); + } + } + }, + Some((1,x)) => { + //TODO logging + error, 1.0 not supported + unimplemented!() + }, + Some(_) => { + //TODO logging + error, version not supported + unimplemented!() + } + }; + + // 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); + + // 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!("")); + list.split(' ').map(|e| e.to_string()).collect::>() + } + }; + + let has_dp_extension = |e: &str| dp_extensions.iter().find(|s| s == &e).is_some(); + + let display = match native { + Native::X11(display, _) if has_dp_extension("EGL_KHR_platform_x11") && + egl.GetPlatformDisplay.is_loaded() => + { + unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, + ptr::null()) } + }, + + Native::X11(display, _) if has_dp_extension("EGL_EXT_platform_x11") && + egl.GetPlatformDisplayEXT.is_loaded() => + { + unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, display as *mut _, + ptr::null()) } + }, + + Native::Gbm(display, _) if has_dp_extension("EGL_KHR_platform_gbm") && + egl.GetPlatformDisplay.is_loaded() => + { + unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, + ptr::null()) } + }, + + Native::Gbm(display, _) if has_dp_extension("EGL_MESA_platform_gbm") && + egl.GetPlatformDisplayEXT.is_loaded() => + { + unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, + ptr::null()) } + }, + + Native::Wayland(display, _) if has_dp_extension("EGL_KHR_platform_wayland") && + egl.GetPlatformDisplay.is_loaded() => + { + unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_WAYLAND_KHR, display as *mut _, + ptr::null()) } + }, + + Native::Wayland(display, _) if has_dp_extension("EGL_EXT_platform_wayland") && + egl.GetPlatformDisplayEXT.is_loaded() => + { + unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_WAYLAND_EXT, display as *mut _, + ptr::null()) } + }, + + Native::X11(display, _) | Native::Gbm(display, _) | + Native::Wayland(display, _) => { + unsafe { egl.GetDisplay(display as *mut _) } + } + }; + + let egl_version = unsafe { + let mut major: ffi::egl::types::EGLint = mem::uninitialized(); + 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"))) + } + + (major, minor) + }; + + // 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!("")); + list.split(' ').map(|e| e.to_string()).collect::>() + + } else { + vec![] + }; + + if egl_version >= (1, 2) { + if egl.BindAPI(ffi::egl::OPENGL_ES_API) == 0 { + return Err(CreationError::OpenGlVersionNotSupported); + } + } + + let descriptor = { + let mut out: Vec = Vec::with_capacity(37); + + if egl_version >= (1, 2) { + out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int); + out.push(ffi::egl::RGB_BUFFER as c_int); + } + + out.push(ffi::egl::SURFACE_TYPE as c_int); + // TODO: Some versions of Mesa report a BAD_ATTRIBUTE error + // if we ask for PBUFFER_BIT as well as WINDOW_BIT + out.push((ffi::egl::WINDOW_BIT) as c_int); + + match version { + (3, _) => { + 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); + }, + (2, _) => { + 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); + }, + (_, _) => unreachable!(), + }; + + if let Some(hardware_accelerated) = reqs.hardware_accelerated { + out.push(ffi::egl::CONFIG_CAVEAT as c_int); + out.push(if hardware_accelerated { + ffi::egl::NONE as c_int + } else { + ffi::egl::SLOW_CONFIG as c_int + }); + } + + if let Some(color) = reqs.color_bits { + out.push(ffi::egl::RED_SIZE as c_int); + out.push((color / 3) as c_int); + out.push(ffi::egl::GREEN_SIZE as c_int); + out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int); + out.push(ffi::egl::BLUE_SIZE as c_int); + out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int); + } + + if let Some(alpha) = reqs.alpha_bits { + out.push(ffi::egl::ALPHA_SIZE as c_int); + out.push(alpha as c_int); + } + + if let Some(depth) = reqs.depth_bits { + out.push(ffi::egl::DEPTH_SIZE as c_int); + out.push(depth as c_int); + } + + if let Some(stencil) = reqs.stencil_bits { + out.push(ffi::egl::STENCIL_SIZE as c_int); + out.push(stencil as c_int); + } + + if let Some(true) = reqs.double_buffer { + return Err(CreationError::NoAvailablePixelFormat); + } + + if let Some(multisampling) = reqs.multisampling { + out.push(ffi::egl::SAMPLES as c_int); + out.push(multisampling as c_int); + } + + if reqs.stereoscopy { + return Err(CreationError::NoAvailablePixelFormat); + } + + out.push(ffi::egl::NONE as c_int); + out + }; + + // 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 { + return Err(CreationError::OsError(format!("eglChooseConfig failed"))); + } + if num_configs == 0 { + return Err(CreationError::NoAvailablePixelFormat); + } + + // 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 + } + ) + }; + + 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, + stereoscopy: false, + double_buffer: true, + 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 + }; + + let surface = unsafe { + let surface = match native { + Native::X11(_, window) => egl.CreateWindowSurface(display, config_id, window, ptr::null()), + Native::Wayland(_, window) => egl.CreateWindowSurface(display, config_id, window, ptr::null()), + Native::Gbm(_, window) => egl.CreateWindowSurface(display, config_id, window, ptr::null()), + }; + + if surface.is_null() { + return Err(CreationError::OsError(format!("eglCreateWindowSurface failed"))) + } + surface + }; + + 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() + { + context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32); + context_attributes.push(version.0 as i32); + context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32); + context_attributes.push(version.1 as i32); + + // handling robustness + let supports_robustness = egl_version >= (1, 5) || + extensions.iter() + .find(|s| s == &"EGL_EXT_create_context_robustness") + .is_some(); + + match attributes.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); + 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; + } 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; + } + }, + + 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; + } 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; + } + }, + } + + if attributes.debug { + if egl_version >= (1, 5) { + context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32); + context_attributes.push(ffi::egl::TRUE as i32); + } + + // 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; + } + + context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32); + context_attributes.push(flags); + + } else if egl_version >= (1, 3) { + // robustness is not supported + match attributes.robustness { + Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + return Err(CreationError::RobustnessNotSupported); + }, + _ => () + } + + context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); + context_attributes.push(version.0 as i32); + } + + context_attributes.push(ffi::egl::NONE as i32); + + let context = egl.CreateContext(display, config_id, ptr::null(), + context_attributes.as_ptr()); + + if context.is_null() { + match egl.GetError() as u32 { + ffi::egl::BAD_ATTRIBUTE => return Err(CreationError::OpenGlVersionNotSupported), + e => panic!("eglCreateContext failed: 0x{:x}", e), + } + } + + Ok(EGLContext { + context: context as *const _, + display: display as *const _, + egl: egl, + surface: surface as *const _, + pixel_format: desc, + }) + } + + pub fn swap_buffers(&self) -> Result<(), SwapBuffersError> { + let ret = unsafe { + self.egl.SwapBuffers(self.display as *const _, self.surface as *const _) + }; + + if ret == 0 { + match unsafe { self.egl.GetError() } as u32 { + ffi::egl::CONTEXT_LOST => return Err(SwapBuffersError::ContextLost), + err => panic!("eglSwapBuffers failed (eglGetError returned 0x{:x})", err) + } + } else { + Ok(()) + } + } + + pub unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { + let addr = CString::new(symbol.as_bytes()).unwrap(); + let addr = addr.as_ptr(); + unsafe { + self.egl.GetProcAddress(addr) as *const _ + } + } + + pub fn is_current(&self) -> bool { + unsafe { self.egl.GetCurrentContext() == self.context as *const _ } + } + + pub unsafe fn make_current(&self) -> Result<(), SwapBuffersError> { + let ret = self.egl.MakeCurrent(self.display as *const _, self.surface as *const _, self.surface as *const _, self.context as *const _); + + if ret == 0 { + match self.egl.GetError() as u32 { + ffi::egl::CONTEXT_LOST => return Err(SwapBuffersError::ContextLost), + err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err) + } + } else { + Ok(()) + } + } + + pub fn get_pixel_format(&self) -> PixelFormat { + self.pixel_format + }} + +unsafe impl Send for EGLContext {} +unsafe impl Sync for EGLContext {} + +impl Drop for EGLContext { + 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.egl.DestroyContext(self.display as *const _, self.context as *const _); + self.egl.DestroySurface(self.display as *const _, self.surface as *const _); + self.egl.Terminate(self.display as *const _); + } + } +} + +/// Error that can happen when swapping buffers. +#[derive(Debug, Clone)] +pub enum SwapBuffersError { + /// The OpenGL context has been lost and needs to be recreated. + /// + /// All the objects associated to it (textures, buffers, programs, etc.) + /// need to be recreated from scratch. + /// + /// Operations will have no effect. Functions that read textures, buffers, etc. + /// from OpenGL will return uninitialized data instead. + /// + /// A context loss usually happens on mobile devices when the user puts the + /// application on sleep and wakes it up later. However any OpenGL implementation + /// can theoretically lose the context at any time. + ContextLost, + /// The buffers have already been swapped. + /// + /// This error can be returned when `swap_buffers` has been called multiple times + /// without any modification in between. + AlreadySwapped, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct GlAttributes { + pub version: Option<(u8, u8)>, + pub profile: Option, + pub debug: bool, + pub robustness: Robustness, + pub vsync: bool, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Robustness { + NotRobust, + NoError, + RobustNoResetNotification, + TryRobustNoResetNotification, + RobustLoseContextOnReset, + TryRobustLoseContextOnReset, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum GlProfile { + Compatibility, + Core, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct PixelFormatRequirements { + pub hardware_accelerated: Option, + pub color_bits: Option, + pub float_color_buffer: bool, + pub alpha_bits: Option, + pub depth_bits: Option, + pub stencil_bits: Option, + pub double_buffer: Option, + pub multisampling: Option, + pub stereoscopy: bool, +} + +/// Describes the pixel format of the main framebuffer +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PixelFormat { + /// is the format hardware accelerated + pub hardware_accelerated: bool, + /// number of bits used for colors + pub color_bits: u8, + /// number of bits used for alpha channel + pub alpha_bits: u8, + /// number of bits used for depth channel + pub depth_bits: u8, + /// number of bits used for stencil buffer + pub stencil_bits: u8, + /// is stereoscopy enabled + pub stereoscopy: bool, + /// is double buffering enabled + pub double_buffer: bool, + /// number of samples used for multisampling if enabled + pub multisampling: Option, + /// is srgb enabled + pub srgb: bool, +} + +/// Trait that describes objects that have an OpenGl context +/// and can be used to render upon +pub trait EGLGraphicsBackend: GraphicsBackend { + /// Swaps buffers at the end of a frame. + fn swap_buffers(&self) -> Result<(), SwapBuffersError>; + + /// Returns the address of an OpenGL function. + /// + /// Supposes that the context has been made current before this function is called. + unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void; + + /// Returns the dimensions of the window, or screen, etc in points. + /// + /// That are the scaled pixels of the underlying graphics backend. + /// For nested compositors this will respect the scaling of the root compositor. + /// For drawing directly onto hardware this unit will be equal to actual pixels. + fn get_framebuffer_dimensions(&self) -> (u32, u32); + + /// Returns true if the OpenGL context is the current one in the thread. + fn is_current(&self) -> bool; + + /// Makes the OpenGL context the current context in the current thread. + /// + /// This function is marked unsafe, because the context cannot be made current + /// on multiple threads. + unsafe fn make_current(&self) -> Result<(), SwapBuffersError>; + + /// Returns the pixel format of the main framebuffer of the context. + fn get_pixel_format(&self) -> PixelFormat; +} diff --git a/src/backend/graphics/mod.rs b/src/backend/graphics/mod.rs index 4ad08f8..35855a2 100644 --- a/src/backend/graphics/mod.rs +++ b/src/backend/graphics/mod.rs @@ -30,4 +30,4 @@ pub trait GraphicsBackend { } pub mod software; -pub mod opengl; +pub mod egl; diff --git a/src/backend/graphics/opengl.rs b/src/backend/graphics/opengl.rs deleted file mode 100644 index e5884f5..0000000 --- a/src/backend/graphics/opengl.rs +++ /dev/null @@ -1,96 +0,0 @@ -//! Common traits and types for opengl rendering on graphics backends - - -use super::GraphicsBackend; -use nix::c_void; - -/// Error that can happen when swapping buffers. -#[derive(Debug, Clone)] -pub enum SwapBuffersError { - /// The OpenGL context has been lost and needs to be recreated. - /// - /// All the objects associated to it (textures, buffers, programs, etc.) - /// need to be recreated from scratch. - /// - /// Operations will have no effect. Functions that read textures, buffers, etc. - /// from OpenGL will return uninitialized data instead. - /// - /// A context loss usually happens on mobile devices when the user puts the - /// application on sleep and wakes it up later. However any OpenGL implementation - /// can theoretically lose the context at any time. - ContextLost, - /// The buffers have already been swapped. - /// - /// This error can be returned when `swap_buffers` has been called multiple times - /// without any modification in between. - AlreadySwapped, -} - -/// All APIs related to OpenGL that you can possibly get -/// through OpenglRenderer implementations -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum Api { - /// The classical OpenGL. Available on Windows, Linux, OS/X. - OpenGl, - /// OpenGL embedded system. Available on Linux, Android. - OpenGlEs, - /// OpenGL for the web. Very similar to OpenGL ES. - WebGl, -} - -/// Describes the pixel format of the main framebuffer -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct PixelFormat { - /// is the format hardware accelerated - pub hardware_accelerated: bool, - /// number of bits used for colors - pub color_bits: u8, - /// number of bits used for alpha channel - pub alpha_bits: u8, - /// number of bits used for depth channel - pub depth_bits: u8, - /// number of bits used for stencil buffer - pub stencil_bits: u8, - /// is stereoscopy enabled - pub stereoscopy: bool, - /// is double buffering enabled - pub double_buffer: bool, - /// number of samples used for multisampling if enabled - pub multisampling: Option, - /// is srgb enabled - pub srgb: bool, -} - -/// Trait that describes objects that have an OpenGl context -/// and can be used to render upon -pub trait OpenglGraphicsBackend: GraphicsBackend { - /// Swaps buffers at the end of a frame. - fn swap_buffers(&self) -> Result<(), SwapBuffersError>; - - /// Returns the address of an OpenGL function. - /// - /// Supposes that the context has been made current before this function is called. - unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void; - - /// Returns the dimensions of the window, or screen, etc in points. - /// - /// That are the scaled pixels of the underlying graphics backend. - /// For nested compositors this will respect the scaling of the root compositor. - /// For drawing directly onto hardware this unit will be equal to actual pixels. - fn get_framebuffer_dimensions(&self) -> (u32, u32); - - /// Returns true if the OpenGL context is the current one in the thread. - fn is_current(&self) -> bool; - - /// Makes the OpenGL context the current context in the current thread. - /// - /// This function is marked unsafe, because the context cannot be made current - /// on multiple threads. - unsafe fn make_current(&self); - - /// Returns the OpenGL API being used. - fn get_api(&self) -> Api; - - /// Returns the pixel format of the main framebuffer of the context. - fn get_pixel_format(&self) -> PixelFormat; -} diff --git a/src/backend/mod.rs b/src/backend/mod.rs index c91fc91..3d1ef0a 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -6,17 +6,18 @@ //! //! Supported graphics backends: //! -//! - glutin (headless/windowed) +//! - winit //! //! Supported input backends: //! -//! - glutin (windowed) +//! - winit +//! - libinput pub mod input; pub mod graphics; -#[cfg(feature = "backend_glutin")] -pub mod glutin; +#[cfg(feature = "backend_winit")] +pub mod winit; #[cfg(feature = "backend_libinput")] pub mod libinput; diff --git a/src/backend/winit.rs b/src/backend/winit.rs new file mode 100644 index 0000000..468e969 --- /dev/null +++ b/src/backend/winit.rs @@ -0,0 +1,655 @@ +use backend::{SeatInternal, TouchSlotInternal}; +use backend::graphics::GraphicsBackend; +use backend::graphics::egl::{CreationError, EGLContext, EGLGraphicsBackend, GlAttributes, Native, Robustness, PixelFormatRequirements, PixelFormat, SwapBuffersError}; +use backend::input::{Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, + KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent, + PointerMotionAbsoluteEvent, Seat, SeatCapabilities, TouchCancelEvent, TouchDownEvent, + TouchMotionEvent, TouchSlot, TouchUpEvent, UnusedEvent}; +use nix::c_void; +use wayland_client::egl as wegl; +use winit::{CreationError as WinitCreationError, ElementState, Event, EventsLoop, MouseButton as WinitMouseButton, MouseCursor, MouseScrollDelta, Touch, TouchPhase, Window, WindowBuilder, WindowEvent}; +use winit::os::unix::WindowExt; + +use std::cmp; +use std::error::Error; +use std::fmt; +use std::rc::Rc; + +/// Window with an active EGL Context created by `winit`. Implements the +/// `EGLGraphicsBackend` graphics backend trait +pub struct WinitGraphicsBackend { + window: Rc, + context: EGLContext, +} + +/// Abstracted event loop of a `winit` `Window` implementing the `InputBackend` trait +/// +/// You need to call `process_new_events` periodically to receive any events. +pub struct WinitInputBackend { + events_loop: EventsLoop, + window: Rc, + surface: Option, + time_counter: u32, + key_counter: u32, + seat: Seat, + input_config: (), + handler: Option + 'static>>, +} + +/// Create a new `WinitGraphicsBackend`, which implements the `EGLGraphicsBackend` +/// graphics backend trait +pub fn init() -> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError> { + init_from_builder(WindowBuilder::new()) +} + +pub fn init_from_builder(builder: WindowBuilder) -> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError> { + init_from_builder_with_gl_attr(builder, GlAttributes { + version: None, + profile: None, + debug: cfg!(debug_assertions), + robustness: Robustness::TryRobustLoseContextOnReset, + vsync: true, + }) +} + +/// Create a new `WinitGraphicsBackend`, which implements the `EGLGraphicsBackend` +/// graphics backend trait, with a given already configured `WindowBuilder` for +/// customization. +pub fn init_from_builder_with_gl_attr(builder: WindowBuilder, attributes: GlAttributes) -> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError> { + let events_loop = EventsLoop::new(); + let window = Rc::new(builder.build(&events_loop)?); + + let (native, surface) = if let (Some(display), Some(window)) = (window.get_xlib_display(), window.get_xlib_window()) { + (Native::X11(display, window), None) + } else if let (Some(display), Some(surface)) = (window.get_wayland_display(), window.get_wayland_client_surface()) { + let (w, h) = window.get_inner_size().unwrap(); + let egl_surface = wegl::WlEglSurface::new(surface, w as i32, h as i32); + (Native::Wayland(display, egl_surface.ptr() as *const _), Some(egl_surface)) + } else { + return Err(CreationError::NotSupported) + }; + + let context = unsafe { EGLContext::new(native, attributes, + PixelFormatRequirements { + hardware_accelerated: Some(true), + color_bits: Some(24), + alpha_bits: Some(8), + double_buffer: Some(true), + ..Default::default() + } + )? }; + + Ok((WinitGraphicsBackend { + window: window.clone(), + context: context, + }, WinitInputBackend { + events_loop: events_loop, + window: window, + surface: surface, + time_counter: 0, + key_counter: 0, + seat: Seat::new(0, + SeatCapabilities { + pointer: true, + keyboard: true, + touch: true, + } + ), + input_config: (), + handler: None, + })) +} + +impl GraphicsBackend for WinitGraphicsBackend { + type CursorFormat = MouseCursor; + + fn set_cursor_position(&mut self, x: u32, y: u32) -> Result<(), ()> { + self.window.set_cursor_position(x as i32, y as i32) + } + + fn set_cursor_representation(&mut self, cursor: Self::CursorFormat) { + self.window.set_cursor(cursor) + } +} + +impl EGLGraphicsBackend for WinitGraphicsBackend { + fn swap_buffers(&self) -> Result<(), SwapBuffersError> { + self.context.swap_buffers() + } + + unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { + self.context.get_proc_address(symbol) + } + + fn get_framebuffer_dimensions(&self) -> (u32, u32) { + self.window.get_inner_size_pixels().expect("Window does not exist anymore") + } + + fn is_current(&self) -> bool { + self.context.is_current() + } + + unsafe fn make_current(&self) -> Result<(), SwapBuffersError> { + self.context.make_current() + } + + fn get_pixel_format(&self) -> PixelFormat { + self.context.get_pixel_format() + } +} + +/// Errors that may happen when driving the event loop of `WinitInputBackend` +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum WinitInputError { + /// The underlying `winit` `Window` was closed. No further events can be processed. + /// + /// See `WinitInputBackend::process_new_events`. + WindowClosed, +} + +impl Error for WinitInputError { + fn description(&self) -> &str { + match *self { + WinitInputError::WindowClosed => "Glutin Window was closed", + } + } +} + +impl fmt::Display for WinitInputError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +/// Winit-Backend internal event wrapping winit's types into a `KeyboardKeyEvent` +pub struct WinitKeyboardInputEvent { + time: u32, + key: u8, + count: u32, + state: ElementState, +} + +impl BackendEvent for WinitKeyboardInputEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl KeyboardKeyEvent for WinitKeyboardInputEvent { + fn key_code(&self) -> u32 { + self.key as u32 + } + + fn state(&self) -> KeyState { + self.state.into() + } + + fn count(&self) -> u32 { + self.count + } +} + +#[derive(Clone)] +/// Winit-Backend internal event wrapping winit's types into a `PointerMotionAbsoluteEvent` +pub struct WinitMouseMovedEvent { + window: Rc, + time: u32, + x: i32, + y: i32, +} + +impl BackendEvent for WinitMouseMovedEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl PointerMotionAbsoluteEvent for WinitMouseMovedEvent { + fn x(&self) -> f64 { + self.x as f64 + } + + fn y(&self) -> f64 { + self.y as f64 + } + + fn x_transformed(&self, width: u32) -> u32 { + cmp::min(self.x * width as i32 / + self.window + .get_inner_size_points() + .unwrap_or((width, 0)) + .0 as i32, + 0) as u32 + } + + fn y_transformed(&self, height: u32) -> u32 { + cmp::min(self.y * height as i32 / + self.window + .get_inner_size_points() + .unwrap_or((0, height)) + .1 as i32, + 0) as u32 + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +/// Winit-Backend internal event wrapping winit's types into a `PointerAxisEvent` +pub struct WinitMouseWheelEvent { + axis: Axis, + time: u32, + delta: MouseScrollDelta, +} + +impl BackendEvent for WinitMouseWheelEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl PointerAxisEvent for WinitMouseWheelEvent { + fn axis(&self) -> Axis { + self.axis + } + + fn source(&self) -> AxisSource { + match self.delta { + MouseScrollDelta::LineDelta(_, _) => AxisSource::Wheel, + MouseScrollDelta::PixelDelta(_, _) => AxisSource::Continuous, + } + } + + fn amount(&self) -> f64 { + match (self.axis, self.delta) { + (Axis::Horizontal, MouseScrollDelta::LineDelta(x, _)) | + (Axis::Horizontal, MouseScrollDelta::PixelDelta(x, _)) => x as f64, + (Axis::Vertical, MouseScrollDelta::LineDelta(_, y)) | + (Axis::Vertical, MouseScrollDelta::PixelDelta(_, y)) => y as f64, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +/// Winit-Backend internal event wrapping winit's types into a `PointerButtonEvent` +pub struct WinitMouseInputEvent { + time: u32, + button: WinitMouseButton, + state: ElementState, +} + +impl BackendEvent for WinitMouseInputEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl PointerButtonEvent for WinitMouseInputEvent { + fn button(&self) -> MouseButton { + self.button.into() + } + + fn state(&self) -> MouseButtonState { + self.state.into() + } +} + +#[derive(Clone)] +/// Winit-Backend internal event wrapping winit's types into a `TouchDownEvent` +pub struct WinitTouchStartedEvent { + window: Rc, + time: u32, + location: (f64, f64), + id: u64, +} + +impl BackendEvent for WinitTouchStartedEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl TouchDownEvent for WinitTouchStartedEvent { + fn slot(&self) -> Option { + Some(TouchSlot::new(self.id)) + } + + fn x(&self) -> f64 { + self.location.0 + } + + fn y(&self) -> f64 { + self.location.1 + } + + fn x_transformed(&self, width: u32) -> u32 { + cmp::min(self.location.0 as i32 * width as i32 / + self.window + .get_inner_size_points() + .unwrap_or((width, 0)) + .0 as i32, + 0) as u32 + } + + fn y_transformed(&self, height: u32) -> u32 { + cmp::min(self.location.1 as i32 * height as i32 / + self.window + .get_inner_size_points() + .unwrap_or((0, height)) + .1 as i32, + 0) as u32 + } +} + +#[derive(Clone)] +/// Winit-Backend internal event wrapping winit's types into a `TouchMotionEvent` +pub struct WinitTouchMovedEvent { + window: Rc, + time: u32, + location: (f64, f64), + id: u64, +} + +impl BackendEvent for WinitTouchMovedEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl TouchMotionEvent for WinitTouchMovedEvent { + fn slot(&self) -> Option { + Some(TouchSlot::new(self.id)) + } + + fn x(&self) -> f64 { + self.location.0 + } + + fn y(&self) -> f64 { + self.location.1 + } + + fn x_transformed(&self, width: u32) -> u32 { + self.location.0 as u32 * width / + self.window + .get_inner_size_points() + .unwrap_or((width, 0)) + .0 + } + + fn y_transformed(&self, height: u32) -> u32 { + self.location.1 as u32 * height / + self.window + .get_inner_size_points() + .unwrap_or((0, height)) + .1 + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +/// Winit-Backend internal event wrapping winit's types into a `TouchUpEvent` +pub struct WinitTouchEndedEvent { + time: u32, + id: u64, +} + +impl BackendEvent for WinitTouchEndedEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl TouchUpEvent for WinitTouchEndedEvent { + fn slot(&self) -> Option { + Some(TouchSlot::new(self.id)) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +/// Winit-Backend internal event wrapping winit's types into a `TouchCancelEvent` +pub struct WinitTouchCancelledEvent { + time: u32, + id: u64, +} + +impl BackendEvent for WinitTouchCancelledEvent { + fn time(&self) -> u32 { + self.time + } +} + +impl TouchCancelEvent for WinitTouchCancelledEvent { + fn slot(&self) -> Option { + Some(TouchSlot::new(self.id)) + } +} + +impl InputBackend for WinitInputBackend { + type InputConfig = (); + type EventError = WinitInputError; + + type KeyboardKeyEvent = WinitKeyboardInputEvent; + type PointerAxisEvent = WinitMouseWheelEvent; + type PointerButtonEvent = WinitMouseInputEvent; + type PointerMotionEvent = UnusedEvent; + type PointerMotionAbsoluteEvent = WinitMouseMovedEvent; + type TouchDownEvent = WinitTouchStartedEvent; + type TouchUpEvent = WinitTouchEndedEvent; + type TouchMotionEvent = WinitTouchMovedEvent; + type TouchCancelEvent = WinitTouchCancelledEvent; + type TouchFrameEvent = UnusedEvent; + + fn set_handler + 'static>(&mut self, mut handler: H) { + if self.handler.is_some() { + self.clear_handler(); + } + handler.on_seat_created(&self.seat); + self.handler = Some(Box::new(handler)); + } + + fn get_handler(&mut self) -> Option<&mut InputHandler> { + self.handler + .as_mut() + .map(|handler| handler as &mut InputHandler) + } + + fn clear_handler(&mut self) { + if let Some(mut handler) = self.handler.take() { + handler.on_seat_destroyed(&self.seat); + } + } + + fn input_config(&mut self) -> &mut Self::InputConfig { + &mut self.input_config + } + + /// Processes new events of the underlying event loop to drive the set `InputHandler`. + /// + /// You need to periodically call this function to keep the underlying event loop and + /// `Window` active. Otherwise the window may no respond to user interaction and no + /// input events will be received by a set `InputHandler`. + /// + /// Returns an error if the `Window` the window has been closed. Calling + /// `process_new_events` again after the `Window` has been closed is considered an + /// application error and unspecified baviour may occur. + /// + /// The linked `WinitGraphicsBackend` will error with a lost Context and should + /// not be used anymore as well. + fn dispatch_new_events(&mut self) -> Result<(), WinitInputError> { + let mut closed = false; + + { + let mut closed_ptr = &mut closed; + let mut key_counter = &mut self.key_counter; + let mut time_counter = &mut self.time_counter; + let seat = &self.seat; + let window = &self.window; + let surface = &self.surface; + let mut handler = self.handler.as_mut(); + + self.events_loop.poll_events(move |event| { + if let Some(ref mut handler) = handler { + let Event::WindowEvent{ event, window_id: _ } = event; + match event { + WindowEvent::Resized(x, y) => { + window.set_inner_size(x, y); + if let Some(wl_surface) = surface.as_ref() { + wl_surface.resize(x as i32, y as i32, 0, 0); + } + } + WindowEvent::KeyboardInput(state, key_code, _, _) => { + match state { + ElementState::Pressed => *key_counter += 1, + ElementState::Released => { + *key_counter = key_counter.checked_sub(1).unwrap_or(0) + } + }; + handler.on_keyboard_key(seat, + WinitKeyboardInputEvent { + time: *time_counter, + key: key_code, + count: *key_counter, + state: state, + }) + } + WindowEvent::MouseMoved(x, y) => { + handler.on_pointer_move_absolute(seat, + WinitMouseMovedEvent { + window: window.clone(), + time: *time_counter, + x: x, + y: y, + }) + } + WindowEvent::MouseWheel(delta, _) => { + let event = WinitMouseWheelEvent { + axis: Axis::Horizontal, + time: *time_counter, + delta: delta, + }; + match delta { + MouseScrollDelta::LineDelta(x, y) | + MouseScrollDelta::PixelDelta(x, y) => { + if x != 0.0 { + handler.on_pointer_axis(seat, event.clone()); + } + if y != 0.0 { + handler.on_pointer_axis(seat, event); + } + } + } + } + WindowEvent::MouseInput(state, button) => { + handler.on_pointer_button(seat, + WinitMouseInputEvent { + time: *time_counter, + button: button, + state: state, + }) + } + WindowEvent::Touch(Touch { + phase: TouchPhase::Started, + location: (x, y), + id, + }) => { + handler.on_touch_down(seat, + WinitTouchStartedEvent { + window: window.clone(), + time: *time_counter, + location: (x, y), + id: id, + }) + } + WindowEvent::Touch(Touch { + phase: TouchPhase::Moved, + location: (x, y), + id, + }) => { + handler.on_touch_motion(seat, + WinitTouchMovedEvent { + window: window.clone(), + time: *time_counter, + location: (x, y), + id: id, + }) + } + WindowEvent::Touch(Touch { + phase: TouchPhase::Ended, + location: (x, y), + id, + }) => { + handler.on_touch_motion(seat, + WinitTouchMovedEvent { + window: window.clone(), + time: *time_counter, + location: (x, y), + id: id, + }); + handler.on_touch_up(seat, + WinitTouchEndedEvent { + time: *time_counter, + id: id, + }); + } + WindowEvent::Touch(Touch { + phase: TouchPhase::Cancelled, + id, + .. + }) => { + handler.on_touch_cancel(seat, + WinitTouchCancelledEvent { + time: *time_counter, + id: id, + }) + } + WindowEvent::Closed => *closed_ptr = true, + _ => {} + } + *time_counter += 1; + } + }); + } + + if closed { + Err(WinitInputError::WindowClosed) + } else { + Ok(()) + } + } +} + +impl From for MouseButton { + fn from(button: WinitMouseButton) -> MouseButton { + match button { + WinitMouseButton::Left => MouseButton::Left, + WinitMouseButton::Right => MouseButton::Right, + WinitMouseButton::Middle => MouseButton::Middle, + WinitMouseButton::Other(num) => MouseButton::Other(num), + } + } +} + +impl From for KeyState { + fn from(state: ElementState) -> Self { + match state { + ElementState::Pressed => KeyState::Pressed, + ElementState::Released => KeyState::Released, + } + } +} + +impl From for MouseButtonState { + fn from(state: ElementState) -> Self { + match state { + ElementState::Pressed => MouseButtonState::Pressed, + ElementState::Released => MouseButtonState::Released, + } + } +} + +impl From for CreationError { + fn from(error: WinitCreationError) -> Self { + match error { + WinitCreationError::OsError(x) => CreationError::OsError(x), + WinitCreationError::NotSupported => CreationError::NotSupported, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 7ac03cd..d29f3c9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,11 +14,15 @@ extern crate nix; extern crate xkbcommon; extern crate tempfile; -#[cfg(feature = "backend_glutin")] -extern crate glutin; +#[cfg(feature = "backend_winit")] +extern crate winit; +#[cfg(feature = "backend_winit")] +extern crate wayland_client; #[cfg(feature = "backend_libinput")] extern crate input; +extern crate libloading; + #[cfg(feature = "renderer_glium")] extern crate glium; From 5bc641852f40cd9d2aad4da228a4ae1d5213bbaf Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sun, 21 May 2017 22:40:15 +0200 Subject: [PATCH 02/13] Bugfixes --- Cargo.toml | 2 +- build.rs | 35 +- examples/simple.rs | 8 +- src/backend/glutin.rs | 745 ------------------------------------ src/backend/graphics/egl.rs | 386 +++++++++++-------- src/backend/libinput.rs | 4 +- src/backend/winit.rs | 391 ++++++++++--------- src/keyboard/mod.rs | 2 +- 8 files changed, 457 insertions(+), 1116 deletions(-) delete mode 100644 src/backend/glutin.rs diff --git a/Cargo.toml b/Cargo.toml index 49f4eac..4ff16bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,6 @@ slog-term = "~1.5" [features] default = ["backend_winit", "backend_libinput", "renderer_glium"] -backend_winit = ["winit", "wayland-server/dlopen", "wayland-client", "wayland-client/dlopen"] +backend_winit = ["winit", "wayland-server/dlopen", "wayland-client/dlopen"] renderer_glium = ["glium"] backend_libinput = ["input"] diff --git a/build.rs b/build.rs index d846dc8..0295cf5 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; @@ -12,19 +12,22 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); 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(); } diff --git a/examples/simple.rs b/examples/simple.rs index e435792..709b6d0 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,12 +1,15 @@ extern crate wayland_server; extern crate smithay; -use smithay::backend::glutin; +use smithay::backend::winit; use smithay::backend::input::InputBackend; use smithay::shm::ShmGlobal; use wayland_server::protocol::wl_shm; fn main() { + // Initialize a simple backend for testing + let (mut renderer, mut input) = winit::init().unwrap(); + let (_, mut event_loop) = wayland_server::create_display(); // Insert the ShmGlobal as a handler to your event loop @@ -24,9 +27,6 @@ fn main() { state.get_handler::(handler_id).get_token() }; - // Initialize a simple backend for testing - let (mut renderer, mut input) = glutin::init_windowed().unwrap(); - // TODO render stuff // TODO put input handling on the event loop diff --git a/src/backend/glutin.rs b/src/backend/glutin.rs deleted file mode 100644 index fe1ba84..0000000 --- a/src/backend/glutin.rs +++ /dev/null @@ -1,745 +0,0 @@ -//! Implementation of backend traits for types provided by `glutin` - - -use backend::{SeatInternal, TouchSlotInternal}; -use backend::graphics::GraphicsBackend; -use backend::graphics::opengl::{Api, OpenglGraphicsBackend, PixelFormat, SwapBuffersError}; -use backend::input::{Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, - KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent, - PointerMotionAbsoluteEvent, Seat, SeatCapabilities, TouchCancelEvent, TouchDownEvent, - TouchMotionEvent, TouchSlot, TouchUpEvent, UnusedEvent}; -use glutin::{Api as GlutinApi, MouseButton as GlutinMouseButton, MouseCursor, - PixelFormat as GlutinPixelFormat}; -use glutin::{ContextError, CreationError, ElementState, Event, GlContext, HeadlessContext, - HeadlessRendererBuilder, MouseScrollDelta, Touch, TouchPhase, Window, WindowBuilder}; -use nix::c_void; -use std::cmp; -use std::error::Error; -use std::fmt; -use std::rc::Rc; - -/// Create a new `GlutinHeadlessRenderer` which implements the `OpenglRenderer` graphics -/// backend trait -pub fn init_headless_renderer() -> Result { - init_headless_renderer_from_builder(HeadlessRendererBuilder::new(1024, 600) -} - -/// Create a new `GlutinHeadlessRenderer`, which implements the `OpenglRenderer` graphics -/// backend trait, with a given already configured `HeadlessRendererBuilder` for -/// customization -pub fn init_headless_renderer_from_builder(builder: HeadlessRendererBuilder) - -> Result { - let (w, h) = builder.dimensions; - let context = builder.build_strict()?; - - Ok(GlutinHeadlessRenderer::new(context, w, h)) -} - -/// Create a new `GlutinWindowedRenderer`, which implements the `OpenglRenderer` graphics -/// backend trait -pub fn init_windowed_renderer() -> Result { - init_windowed_renderer_from_builder(WindowBuilder::new()) -} - -/// Create a new `GlutinWindowedRenderer`, which implements the `OpenglRenderer` graphics -/// backend trait, with a given already configured `WindowBuilder` for customization. -pub fn init_windowed_renderer_from_builder(builder: WindowBuilder) - -> Result { - let window = Rc::new(builder.build_strict()?); - Ok(GlutinWindowedRenderer::new(window)) -} - -/// Create a new `glutin` `Window`. Returns a `GlutinWindowedRenderer` implementing -/// the `OpenglRenderer` graphics backend trait and a `GlutinInputBackend` implementing -/// the `InputBackend` trait. -pub fn init_windowed() -> Result<(GlutinWindowedRenderer, GlutinInputBackend), CreationError> { - init_windowed_from_builder(WindowBuilder::new()) -} - -/// Create a new `glutin` `Window` with a given already configured `WindowBuilder` for -/// customization. Returns a `GlutinWindowedRenderer` implementing -/// the `OpenglRenderer` graphics backend trait and a `GlutinInputBackend` implementing -/// the `InputBackend` trait. -pub fn init_windowed_from_builder(builder: WindowBuilder) - -> Result<(GlutinWindowedRenderer, GlutinInputBackend), CreationError> { - let window = Rc::new(builder.build_strict()?); - Ok((GlutinWindowedRenderer::new(window.clone()), GlutinInputBackend::new(window))) -} - -/// Headless Opengl Context created by `glutin`. Implements the `OpenglGraphicsBackend` graphics -/// backend trait. -pub struct GlutinHeadlessRenderer { - context: HeadlessContext, - w: u32, - h: u32, -} - -impl GlutinHeadlessRenderer { - fn new(context: HeadlessContext, w: u32, h: u32) -> GlutinHeadlessRenderer { - GlutinHeadlessRenderer { - context: context, - w: w, - h: h, - } - } -} - -impl GraphicsBackend for GlutinHeadlessRenderer { - type CursorFormat = (); - - fn set_cursor_position(&mut self, _x: u32, _y: u32) -> Result<(), ()> { - // FIXME: Maybe save position? Is it of any use? - Ok(()) - } - - fn set_cursor_representation(&mut self, _cursor: ()) {} -} - -impl OpenglGraphicsBackend for GlutinHeadlessRenderer { - #[inline] - fn swap_buffers(&self) -> Result<(), SwapBuffersError> { - match self.context.swap_buffers() { - Ok(()) => Ok(()), - Err(ContextError::IoError(e)) => panic!("Error while swapping buffers: {:?}", e), - Err(ContextError::ContextLost) => Err(SwapBuffersError::ContextLost), - } - } - - #[inline] - unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { - self.context.get_proc_address(symbol) as *const _ - } - - #[inline] - fn get_framebuffer_dimensions(&self) -> (u32, u32) { - (self.w, self.h) - } - - #[inline] - fn is_current(&self) -> bool { - self.context.is_current() - } - - #[inline] - unsafe fn make_current(&self) { - self.context.make_current().unwrap(); - } - - fn get_api(&self) -> Api { - self.context.get_api().into() - } - - fn get_pixel_format(&self) -> PixelFormat { - self.context.get_pixel_format().into() - } -} - -/// Window with an active Opengl Context created by `glutin`. Implements the -/// `OpenglGraphicsBackend` graphics backend trait. -pub struct GlutinWindowedRenderer { - window: Rc, -} - -impl GlutinWindowedRenderer { - fn new(window: Rc) -> GlutinWindowedRenderer { - GlutinWindowedRenderer { window: window } - } -} - -impl GraphicsBackend for GlutinWindowedRenderer { - type CursorFormat = MouseCursor; - - fn set_cursor_position(&mut self, x: u32, y: u32) -> Result<(), ()> { - if let Some((win_x, win_y)) = self.window.get_position() { - self.window - .set_cursor_position(win_x + x as i32, win_y + y as i32) - } else { - Err(()) - } - } - - fn set_cursor_representation(&mut self, cursor: MouseCursor) { - self.window.set_cursor(cursor); - } -} - -impl OpenglGraphicsBackend for GlutinWindowedRenderer { - #[inline] - fn swap_buffers(&self) -> Result<(), SwapBuffersError> { - match self.window.swap_buffers() { - Ok(()) => Ok(()), - Err(ContextError::IoError(e)) => panic!("Error while swapping buffers: {:?}", e), - Err(ContextError::ContextLost) => Err(SwapBuffersError::ContextLost), - } - } - - #[inline] - unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { - self.window.get_proc_address(symbol) as *const _ - } - - #[inline] - fn get_framebuffer_dimensions(&self) -> (u32, u32) { - let (width, height) = self.window.get_inner_size().unwrap_or((800, 600)); // TODO: 800x600 ? - let scale = self.window.hidpi_factor(); - ((width as f32 * scale) as u32, (height as f32 * scale) as u32) - } - - #[inline] - fn is_current(&self) -> bool { - self.window.is_current() - } - - #[inline] - unsafe fn make_current(&self) { - self.window.make_current().unwrap(); - } - - fn get_api(&self) -> Api { - self.window.get_api().into() - } - - fn get_pixel_format(&self) -> PixelFormat { - self.window.get_pixel_format().into() - } -} - -/// Errors that may happen when driving the event loop of `GlutinInputBackend` -#[derive(Debug)] -pub enum GlutinInputError { - /// The underlying `glutin` `Window` was closed. No further events can be processed. - /// - /// See `GlutinInputBackend::process_new_events`. - WindowClosed, -} - -impl Error for GlutinInputError { - fn description(&self) -> &str { - match *self { - GlutinInputError::WindowClosed => "Glutin Window was closed", - } - } -} - -impl fmt::Display for GlutinInputError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description()) - } -} - -/// Abstracted event loop of a `glutin` `Window` implementing the `InputBackend` trait -/// -/// You need to call `process_new_events` periodically to receive any events. -pub struct GlutinInputBackend { - window: Rc, - time_counter: u32, - key_counter: u32, - seat: Seat, - input_config: (), - handler: Option + 'static>>, -} - -#[derive(Clone)] -/// Glutin-Backend internal event wrapping glutin's types into a `KeyboardKeyEvent` -pub struct GlutinKeyboardInputEvent { - time: u32, - key: u8, - count: u32, - state: ElementState, -} - -impl BackendEvent for GlutinKeyboardInputEvent { - fn time(&self) -> u32 { - self.time - } -} - -impl KeyboardKeyEvent for GlutinKeyboardInputEvent { - fn key_code(&self) -> u32 { - self.key as u32 - } - - fn state(&self) -> KeyState { - self.state.into() - } - - fn count(&self) -> u32 { - self.count - } -} - -#[derive(Clone)] -/// Glutin-Backend internal event wrapping glutin's types into a `PointerMotionAbsoluteEvent` -pub struct GlutinMouseMovedEvent { - window: Rc, - time: u32, - x: i32, - y: i32, -} - -impl BackendEvent for GlutinMouseMovedEvent { - fn time(&self) -> u32 { - self.time - } -} - -impl PointerMotionAbsoluteEvent for GlutinMouseMovedEvent { - fn x(&self) -> f64 { - self.x as f64 - } - - fn y(&self) -> f64 { - self.y as f64 - } - - fn x_transformed(&self, width: u32) -> u32 { - cmp::min(self.x * width as i32 / - self.window - .get_inner_size_points() - .unwrap_or((width, 0)) - .0 as i32, - 0) as u32 - } - - fn y_transformed(&self, height: u32) -> u32 { - cmp::min(self.y * height as i32 / - self.window - .get_inner_size_points() - .unwrap_or((0, height)) - .1 as i32, - 0) as u32 - } -} - -#[derive(Clone)] -/// Glutin-Backend internal event wrapping glutin's types into a `PointerAxisEvent` -pub struct GlutinMouseWheelEvent { - axis: Axis, - time: u32, - delta: MouseScrollDelta, -} - -impl BackendEvent for GlutinMouseWheelEvent { - fn time(&self) -> u32 { - self.time - } -} - -impl PointerAxisEvent for GlutinMouseWheelEvent { - fn axis(&self) -> Axis { - self.axis - } - - fn source(&self) -> AxisSource { - match self.delta { - MouseScrollDelta::LineDelta(_, _) => AxisSource::Wheel, - MouseScrollDelta::PixelDelta(_, _) => AxisSource::Continuous, - } - } - - fn amount(&self) -> f64 { - match (self.axis, self.delta) { - (Axis::Horizontal, MouseScrollDelta::LineDelta(x, _)) | - (Axis::Horizontal, MouseScrollDelta::PixelDelta(x, _)) => x as f64, - (Axis::Vertical, MouseScrollDelta::LineDelta(_, y)) | - (Axis::Vertical, MouseScrollDelta::PixelDelta(_, y)) => y as f64, - } - } -} - -#[derive(Clone)] -/// Glutin-Backend internal event wrapping glutin's types into a `PointerButtonEvent` -pub struct GlutinMouseInputEvent { - time: u32, - button: GlutinMouseButton, - state: ElementState, -} - -impl BackendEvent for GlutinMouseInputEvent { - fn time(&self) -> u32 { - self.time - } -} - -impl PointerButtonEvent for GlutinMouseInputEvent { - fn button(&self) -> MouseButton { - self.button.into() - } - - fn state(&self) -> MouseButtonState { - self.state.into() - } -} - -#[derive(Clone)] -/// Glutin-Backend internal event wrapping glutin's types into a `TouchDownEvent` -pub struct GlutinTouchStartedEvent { - window: Rc, - time: u32, - location: (f64, f64), - id: u64, -} - -impl BackendEvent for GlutinTouchStartedEvent { - fn time(&self) -> u32 { - self.time - } -} - -impl TouchDownEvent for GlutinTouchStartedEvent { - fn slot(&self) -> Option { - Some(TouchSlot::new(self.id)) - } - - fn x(&self) -> f64 { - self.location.0 - } - - fn y(&self) -> f64 { - self.location.1 - } - - fn x_transformed(&self, width: u32) -> u32 { - cmp::min(self.location.0 as i32 * width as i32 / - self.window - .get_inner_size_points() - .unwrap_or((width, 0)) - .0 as i32, - 0) as u32 - } - - fn y_transformed(&self, height: u32) -> u32 { - cmp::min(self.location.1 as i32 * height as i32 / - self.window - .get_inner_size_points() - .unwrap_or((0, height)) - .1 as i32, - 0) as u32 - } -} - -#[derive(Clone)] -/// Glutin-Backend internal event wrapping glutin's types into a `TouchMotionEvent` -pub struct GlutinTouchMovedEvent { - window: Rc, - time: u32, - location: (f64, f64), - id: u64, -} - -impl BackendEvent for GlutinTouchMovedEvent { - fn time(&self) -> u32 { - self.time - } -} - -impl TouchMotionEvent for GlutinTouchMovedEvent { - fn slot(&self) -> Option { - Some(TouchSlot::new(self.id)) - } - - fn x(&self) -> f64 { - self.location.0 - } - - fn y(&self) -> f64 { - self.location.1 - } - - fn x_transformed(&self, width: u32) -> u32 { - self.location.0 as u32 * width / - self.window - .get_inner_size_points() - .unwrap_or((width, 0)) - .0 - } - - fn y_transformed(&self, height: u32) -> u32 { - self.location.1 as u32 * height / - self.window - .get_inner_size_points() - .unwrap_or((0, height)) - .1 - } -} - -#[derive(Clone)] -/// Glutin-Backend internal event wrapping glutin's types into a `TouchUpEvent` -pub struct GlutinTouchEndedEvent { - time: u32, - id: u64, -} - -impl BackendEvent for GlutinTouchEndedEvent { - fn time(&self) -> u32 { - self.time - } -} - -impl TouchUpEvent for GlutinTouchEndedEvent { - fn slot(&self) -> Option { - Some(TouchSlot::new(self.id)) - } -} - -#[derive(Clone)] -/// Glutin-Backend internal event wrapping glutin's types into a `TouchCancelEvent` -pub struct GlutinTouchCancelledEvent { - time: u32, - id: u64, -} - -impl BackendEvent for GlutinTouchCancelledEvent { - fn time(&self) -> u32 { - self.time - } -} - -impl TouchCancelEvent for GlutinTouchCancelledEvent { - fn slot(&self) -> Option { - Some(TouchSlot::new(self.id)) - } -} - -impl InputBackend for GlutinInputBackend { - type InputConfig = (); - type EventError = GlutinInputError; - - type KeyboardKeyEvent = GlutinKeyboardInputEvent; - type PointerAxisEvent = GlutinMouseWheelEvent; - type PointerButtonEvent = GlutinMouseInputEvent; - type PointerMotionEvent = UnusedEvent; - type PointerMotionAbsoluteEvent = GlutinMouseMovedEvent; - type TouchDownEvent = GlutinTouchStartedEvent; - type TouchUpEvent = GlutinTouchEndedEvent; - type TouchMotionEvent = GlutinTouchMovedEvent; - type TouchCancelEvent = GlutinTouchCancelledEvent; - type TouchFrameEvent = UnusedEvent; - - fn set_handler + 'static>(&mut self, mut handler: H) { - if self.handler.is_some() { - self.clear_handler(); - } - handler.on_seat_created(&self.seat); - self.handler = Some(Box::new(handler)); - } - - fn get_handler(&mut self) -> Option<&mut InputHandler> { - self.handler - .as_mut() - .map(|handler| handler as &mut InputHandler) - } - - fn clear_handler(&mut self) { - if let Some(mut handler) = self.handler.take() { - handler.on_seat_destroyed(&self.seat); - } - } - - fn input_config(&mut self) -> &mut Self::InputConfig { - &mut self.input_config - } - - /// Processes new events of the underlying event loop to drive the set `InputHandler`. - /// - /// You need to periodically call this function to keep the underlying event loop and - /// `Window` active. Otherwise the window may no respond to user interaction and no - /// input events will be received by a set `InputHandler`. - /// - /// Returns an error if the `Window` the window has been closed. Calling - /// `process_new_events` again after the `Window` has been closed is considered an - /// application error and unspecified baviour may occur. - /// - /// The linked `GlutinWindowedRenderer` will error with a lost Context and should - /// not be used anymore as well. - fn dispatch_new_events(&mut self) -> Result<(), GlutinInputError> { - for event in self.window.poll_events() { - if let Some(ref mut handler) = self.handler { - match event { - Event::KeyboardInput(state, key_code, _) => { - match state { - ElementState::Pressed => self.key_counter += 1, - ElementState::Released => { - self.key_counter = self.key_counter.checked_sub(1).unwrap_or(0) - } - }; - handler.on_keyboard_key(&self.seat, - GlutinKeyboardInputEvent { - time: self.time_counter, - key: key_code, - count: self.key_counter, - state: state, - }) - } - Event::MouseMoved(x, y) => { - handler.on_pointer_move_absolute(&self.seat, - GlutinMouseMovedEvent { - window: self.window.clone(), - time: self.time_counter, - x: x, - y: y, - }) - } - Event::MouseWheel(delta, _) => { - let event = GlutinMouseWheelEvent { - axis: Axis::Horizontal, - time: self.time_counter, - delta: delta, - }; - match delta { - MouseScrollDelta::LineDelta(x, y) | - MouseScrollDelta::PixelDelta(x, y) => { - if x != 0.0 { - handler.on_pointer_axis(&self.seat, event.clone()); - } - if y != 0.0 { - handler.on_pointer_axis(&self.seat, event); - } - } - } - } - Event::MouseInput(state, button) => { - handler.on_pointer_button(&self.seat, - GlutinMouseInputEvent { - time: self.time_counter, - button: button, - state: state, - }) - } - Event::Touch(Touch { - phase: TouchPhase::Started, - location: (x, y), - id, - }) => { - handler.on_touch_down(&self.seat, - GlutinTouchStartedEvent { - window: self.window.clone(), - time: self.time_counter, - location: (x, y), - id: id, - }) - } - Event::Touch(Touch { - phase: TouchPhase::Moved, - location: (x, y), - id, - }) => { - handler.on_touch_motion(&self.seat, - GlutinTouchMovedEvent { - window: self.window.clone(), - time: self.time_counter, - location: (x, y), - id: id, - }) - } - Event::Touch(Touch { - phase: TouchPhase::Ended, - location: (x, y), - id, - }) => { - handler.on_touch_motion(&self.seat, - GlutinTouchMovedEvent { - window: self.window.clone(), - time: self.time_counter, - location: (x, y), - id: id, - }); - handler.on_touch_up(&self.seat, - GlutinTouchEndedEvent { - time: self.time_counter, - id: id, - }); - } - Event::Touch(Touch { - phase: TouchPhase::Cancelled, - id, - .. - }) => { - handler.on_touch_cancel(&self.seat, - GlutinTouchCancelledEvent { - time: self.time_counter, - id: id, - }) - } - Event::Closed => return Err(GlutinInputError::WindowClosed), - _ => {} - } - self.time_counter += 1; - } - } - Ok(()) - } -} - -impl GlutinInputBackend { - fn new(window: Rc) -> GlutinInputBackend { - GlutinInputBackend { - window: window, - time_counter: 0, - key_counter: 0, - seat: Seat::new(0, - SeatCapabilities { - pointer: true, - keyboard: true, - touch: true, - }), - input_config: (), - handler: None, - } - } -} - -impl From for Api { - fn from(api: GlutinApi) -> Self { - match api { - GlutinApi::OpenGl => Api::OpenGl, - GlutinApi::OpenGlEs => Api::OpenGlEs, - GlutinApi::WebGl => Api::WebGl, - } - } -} - -impl From for PixelFormat { - fn from(format: GlutinPixelFormat) -> Self { - PixelFormat { - hardware_accelerated: format.hardware_accelerated, - color_bits: format.color_bits, - alpha_bits: format.alpha_bits, - depth_bits: format.depth_bits, - stencil_bits: format.stencil_bits, - stereoscopy: format.stereoscopy, - double_buffer: format.double_buffer, - multisampling: format.multisampling, - srgb: format.srgb, - } - } -} - -impl From for MouseButton { - fn from(button: GlutinMouseButton) -> MouseButton { - match button { - GlutinMouseButton::Left => MouseButton::Left, - GlutinMouseButton::Right => MouseButton::Right, - GlutinMouseButton::Middle => MouseButton::Middle, - GlutinMouseButton::Other(num) => MouseButton::Other(num), - } - } -} - -impl From for KeyState { - fn from(state: ElementState) -> Self { - match state { - ElementState::Pressed => KeyState::Pressed, - ElementState::Released => KeyState::Released, - } - } -} - -impl From for MouseButtonState { - fn from(state: ElementState) -> Self { - match state { - ElementState::Pressed => MouseButtonState::Pressed, - ElementState::Released => MouseButtonState::Released, - } - } -} diff --git a/src/backend/graphics/egl.rs b/src/backend/graphics/egl.rs index 694a1d0..0b67345 100644 --- a/src/backend/graphics/egl.rs +++ b/src/backend/graphics/egl.rs @@ -1,27 +1,27 @@ -//! Common traits and types for opengl rendering on graphics backends +//! Common traits and types for egl context creation and rendering /// Large parts of the following file are taken from -/// https://github.com/tomaka/glutin/tree/master/src/api/egl at commit -/// `044e651edf67a2029eecc650dd42546af1501414` +/// https://github.com/tomaka/glutin/tree/044e651edf67a2029eecc650dd42546af1501414/src/api/egl/ /// -/// It therefor falls under glutin's Apache 2.0 license -/// (see https://github.com/tomaka/glutin/blob/master/LICENSE) +/// It therefore falls under glutin's Apache 2.0 license +/// (see https://github.com/tomaka/glutin/tree/044e651edf67a2029eecc650dd42546af1501414/LICENSE) use super::GraphicsBackend; use libloading::Library; use nix::{c_int, c_void}; +use std::error::{self, Error}; use std::ffi::{CStr, CString}; -use std::error::{self, Error}; use std::fmt; use std::io; -use std::ptr; use std::mem; +use std::ptr; +#[allow(non_camel_case_types, dead_code)] mod ffi { use nix::c_void; - use nix::libc::{uint64_t, int32_t, c_long}; + use nix::libc::{c_long, int32_t, uint64_t}; pub type khronos_utime_nanoseconds_t = khronos_uint64_t; pub type khronos_uint64_t = uint64_t; @@ -41,20 +41,32 @@ mod ffi { } } +/// Native types to create an `EGLContext` from. +/// Currently supported providers are X11, Wayland and GBM. #[derive(Clone, Copy)] pub enum Native { + /// X11 Display and Window objects to create an `EGLContext` upon. X11(ffi::NativeDisplayType, ffi::NativeWindowType), + /// Wayland Display and Surface objects to create an `EGLContext` upon. Wayland(ffi::NativeDisplayType, ffi::NativeWindowType), + /// GBM Display Gbm(ffi::NativeDisplayType, ffi::NativeWindowType), } +/// Error that can happen while creating an `EGLContext` #[derive(Debug)] pub enum CreationError { + /// I/O error from the underlying system IoError(io::Error), + /// Operating System error OsError(String), + /// Robustness was requested but is not supported by the graphics system RobustnessNotSupported, + /// The requested OpenGl version is not supported by the graphics system OpenGlVersionNotSupported, + /// There is no pixel format available that fulfills all requirements NoAvailablePixelFormat, + /// Context creation is not supported on this system NotSupported, } @@ -78,13 +90,19 @@ impl error::Error for CreationError { fn description(&self) -> &str { match *self { CreationError::IoError(ref err) => err.description(), - CreationError::OsError(ref text) => &text, - 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 criterias.", + CreationError::OsError(ref text) => text, + 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 criterias." + } CreationError::NotSupported => "Context creation is not supported on the current window system", } } @@ -92,11 +110,12 @@ impl error::Error for CreationError { fn cause(&self) -> Option<&error::Error> { match *self { CreationError::IoError(ref err) => Some(err), - _ => None + _ => None, } } } +/// EGL context for rendering pub struct EGLContext { context: *const c_void, display: *const c_void, @@ -106,12 +125,23 @@ pub struct EGLContext { } impl EGLContext { - pub unsafe fn new(native: Native, mut attributes: GlAttributes, reqs: PixelFormatRequirements) -> Result { + /// Create a new EGL context + /// + /// # Unsafety + /// + /// This method is marked unsafe, because the contents of `Native` cannot be verified and msy + /// contain dangeling pointers are similar unsafe content + pub unsafe fn new(native: Native, mut attributes: GlAttributes, reqs: PixelFormatRequirements) + -> Result { let lib = Library::new("libEGL.so.1")?; let egl = ffi::egl::Egl::load_with(|sym| { - let sym = CString::new(sym).unwrap(); - unsafe { &*lib.get(sym.as_bytes()).unwrap() as *const _ } - }); + let name = CString::new(sym).unwrap(); + let symbol = lib.get::<*mut c_void>(name.as_bytes()); + match symbol { + Ok(x) => *x as *const _, + Err(_) => ptr::null(), + } + }); // If no version is given, try OpenGLES 3.0, if available, // fallback to 2.0 otherwise @@ -119,28 +149,28 @@ impl EGLContext { Some((3, x)) => (3, x), Some((2, x)) => (2, x), None => { - attributes.version = Some((3,0)); + attributes.version = Some((3, 0)); match EGLContext::new(native, attributes, reqs) { Ok(x) => return Ok(x), Err(_) => { - //TODO log - attributes.version = Some((2,0)); + // TODO log + attributes.version = Some((2, 0)); return EGLContext::new(native, attributes, reqs); } } - }, - Some((1,x)) => { - //TODO logging + error, 1.0 not supported + } + Some((1, _)) => { + // TODO logging + error, 1.0 not supported unimplemented!() - }, + } Some(_) => { - //TODO logging + error, version not supported + // TODO logging + error, version not supported unimplemented!() } }; // the first step is to query the list of extensions without any display, if supported - let dp_extensions = unsafe { + let dp_extensions = { 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 @@ -149,68 +179,59 @@ impl EGLContext { 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(|_| String::new()); 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().any(|s| s == e); let display = match native { Native::X11(display, _) if has_dp_extension("EGL_KHR_platform_x11") && - egl.GetPlatformDisplay.is_loaded() => - { - unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, - ptr::null()) } - }, + egl.GetPlatformDisplay.is_loaded() => { + egl.GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, ptr::null()) + } Native::X11(display, _) if has_dp_extension("EGL_EXT_platform_x11") && - egl.GetPlatformDisplayEXT.is_loaded() => - { - unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, display as *mut _, - ptr::null()) } - }, + egl.GetPlatformDisplayEXT.is_loaded() => { + egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, display as *mut _, ptr::null()) + } Native::Gbm(display, _) if has_dp_extension("EGL_KHR_platform_gbm") && - egl.GetPlatformDisplay.is_loaded() => - { - unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, - ptr::null()) } - }, + egl.GetPlatformDisplay.is_loaded() => { + egl.GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, ptr::null()) + } Native::Gbm(display, _) if has_dp_extension("EGL_MESA_platform_gbm") && - egl.GetPlatformDisplayEXT.is_loaded() => - { - unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, - ptr::null()) } - }, + egl.GetPlatformDisplayEXT.is_loaded() => { + egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, ptr::null()) + } Native::Wayland(display, _) if has_dp_extension("EGL_KHR_platform_wayland") && - egl.GetPlatformDisplay.is_loaded() => - { - unsafe { egl.GetPlatformDisplay(ffi::egl::PLATFORM_WAYLAND_KHR, display as *mut _, - ptr::null()) } - }, + egl.GetPlatformDisplay.is_loaded() => { + egl.GetPlatformDisplay(ffi::egl::PLATFORM_WAYLAND_KHR, + display as *mut _, + ptr::null()) + } Native::Wayland(display, _) if has_dp_extension("EGL_EXT_platform_wayland") && - egl.GetPlatformDisplayEXT.is_loaded() => - { - unsafe { egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_WAYLAND_EXT, display as *mut _, - ptr::null()) } - }, - - Native::X11(display, _) | Native::Gbm(display, _) | - Native::Wayland(display, _) => { - unsafe { egl.GetDisplay(display as *mut _) } + egl.GetPlatformDisplayEXT.is_loaded() => { + egl.GetPlatformDisplayEXT(ffi::egl::PLATFORM_WAYLAND_EXT, + display as *mut _, + ptr::null()) } + + Native::X11(display, _) | + Native::Gbm(display, _) | + Native::Wayland(display, _) => egl.GetDisplay(display as *mut _), }; - let egl_version = unsafe { + let egl_version = { let mut major: ffi::egl::types::EGLint = mem::uninitialized(); 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(String::from("eglInitialize failed"))); } (major, minor) @@ -219,18 +240,16 @@ impl EGLContext { // 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 = CStr::from_ptr(egl.QueryString(display, ffi::egl::EXTENSIONS as i32)); + let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new()); list.split(' ').map(|e| e.to_string()).collect::>() } else { vec![] }; - if egl_version >= (1, 2) { - if egl.BindAPI(ffi::egl::OPENGL_ES_API) == 0 { - return Err(CreationError::OpenGlVersionNotSupported); - } + if egl_version >= (1, 2) && egl.BindAPI(ffi::egl::OPENGL_ES_API) == 0 { + return Err(CreationError::OpenGlVersionNotSupported); } let descriptor = { @@ -248,29 +267,33 @@ impl EGLContext { match version { (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); - }, + } (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); - }, + } (_, _) => unreachable!(), }; if let Some(hardware_accelerated) = reqs.hardware_accelerated { out.push(ffi::egl::CONFIG_CAVEAT as c_int); out.push(if hardware_accelerated { - ffi::egl::NONE as c_int - } else { - ffi::egl::SLOW_CONFIG as c_int - }); + ffi::egl::NONE as c_int + } else { + ffi::egl::SLOW_CONFIG as c_int + }); } if let Some(color) = reqs.color_bits { @@ -317,8 +340,12 @@ impl EGLContext { // 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 { - return Err(CreationError::OsError(format!("eglChooseConfig failed"))); + if egl.ChooseConfig(display, + descriptor.as_ptr(), + &mut config_id, + 1, + &mut num_configs) == 0 { + return Err(CreationError::OsError(String::from("eglChooseConfig failed"))); } if num_configs == 0 { return Err(CreationError::NoAvailablePixelFormat); @@ -332,7 +359,7 @@ impl EGLContext { let res = $egl.GetConfigAttrib($display, $config, $attr as ffi::egl::types::EGLint, &mut value); if res == 0 { - return Err(CreationError::OsError(format!("eglGetConfigAttrib failed"))); + return Err(CreationError::OsError(String::from("eglGetConfigAttrib failed"))); } value } @@ -340,8 +367,8 @@ impl EGLContext { }; let desc = PixelFormat { - hardware_accelerated: attrib!(egl, display, config_id, ffi::egl::CONFIG_CAVEAT) - != ffi::egl::SLOW_CONFIG as i32, + 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, @@ -354,18 +381,17 @@ impl EGLContext { 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 }; - let surface = unsafe { + let surface = { let surface = match native { - Native::X11(_, window) => egl.CreateWindowSurface(display, config_id, window, ptr::null()), - Native::Wayland(_, window) => egl.CreateWindowSurface(display, config_id, window, ptr::null()), - Native::Gbm(_, window) => egl.CreateWindowSurface(display, config_id, window, ptr::null()), + Native::X11(_, window) | Native::Wayland(_, window) | Native::Gbm(_, window) => + egl.CreateWindowSurface(display, config_id, window, ptr::null()), }; if surface.is_null() { - return Err(CreationError::OsError(format!("eglCreateWindowSurface failed"))) + return Err(CreationError::OsError(String::from("eglCreateWindowSurface failed"))); } surface }; @@ -373,9 +399,10 @@ impl EGLContext { 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() + .any(|s| s == &"EGL_KHR_create_context") { context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32); context_attributes.push(version.0 as i32); context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32); @@ -383,72 +410,66 @@ impl EGLContext { // handling robustness let supports_robustness = egl_version >= (1, 5) || - extensions.iter() - .find(|s| s == &"EGL_EXT_create_context_robustness") - .is_some(); + extensions + .iter() + .any(|s| s == "EGL_EXT_create_context_robustness"); match attributes.robustness { Robustness::NotRobust => (), Robustness::NoError => { - if extensions.iter().find(|s| s == &"EGL_KHR_create_context_no_error").is_some() { + if extensions + .iter() + .any(|s| s == "EGL_KHR_create_context_no_error") { 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::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; + 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::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; + 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::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; + 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::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; + flags |= ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int; } - }, + } } - if attributes.debug { - if egl_version >= (1, 5) { - context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32); - context_attributes.push(ffi::egl::TRUE as i32); - } - - // 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; + if attributes.debug && egl_version >= (1, 5) { + context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32); + context_attributes.push(ffi::egl::TRUE as i32); } context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32); @@ -457,10 +478,11 @@ impl EGLContext { } else if egl_version >= (1, 3) { // robustness is not supported match attributes.robustness { - Robustness::RobustNoResetNotification | Robustness::RobustLoseContextOnReset => { + Robustness::RobustNoResetNotification | + Robustness::RobustLoseContextOnReset => { return Err(CreationError::RobustnessNotSupported); - }, - _ => () + } + _ => (), } context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); @@ -469,8 +491,7 @@ impl EGLContext { 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, ptr::null(), context_attributes.as_ptr()); if context.is_null() { match egl.GetError() as u32 { @@ -480,57 +501,73 @@ impl EGLContext { } Ok(EGLContext { - context: context as *const _, - display: display as *const _, - egl: egl, - surface: surface as *const _, - pixel_format: desc, - }) + context: context as *const _, + display: display as *const _, + egl: egl, + surface: surface as *const _, + pixel_format: desc, + }) } + /// Swaps buffers at the end of a frame. pub fn swap_buffers(&self) -> Result<(), SwapBuffersError> { let ret = unsafe { - self.egl.SwapBuffers(self.display as *const _, self.surface as *const _) + self.egl + .SwapBuffers(self.display as *const _, self.surface as *const _) }; if ret == 0 { match unsafe { self.egl.GetError() } as u32 { - ffi::egl::CONTEXT_LOST => return Err(SwapBuffersError::ContextLost), - err => panic!("eglSwapBuffers failed (eglGetError returned 0x{:x})", err) + ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost), + err => panic!("eglSwapBuffers failed (eglGetError returned 0x{:x})", err), } } else { Ok(()) } } + /// Returns the address of an OpenGL function. + /// + /// Supposes that the context has been made current before this function is called. pub unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { let addr = CString::new(symbol.as_bytes()).unwrap(); let addr = addr.as_ptr(); - unsafe { - self.egl.GetProcAddress(addr) as *const _ - } + self.egl.GetProcAddress(addr) as *const _ } + /// Returns true if the OpenGL context is the current one in the thread. pub fn is_current(&self) -> bool { unsafe { self.egl.GetCurrentContext() == self.context as *const _ } } + /// Makes the OpenGL context the current context in the current thread. + /// + /// # Unsafety + /// + /// This function is marked unsafe, because the context cannot be made current + /// on multiple threads. pub unsafe fn make_current(&self) -> Result<(), SwapBuffersError> { - let ret = self.egl.MakeCurrent(self.display as *const _, self.surface as *const _, self.surface as *const _, self.context as *const _); + let ret = self.egl + .MakeCurrent(self.display as *const _, + self.surface as *const _, + self.surface as *const _, + self.context as *const _); if ret == 0 { match self.egl.GetError() as u32 { - ffi::egl::CONTEXT_LOST => return Err(SwapBuffersError::ContextLost), - err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err) + ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost), + err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err), } } else { Ok(()) } } + /// Returns the pixel format of the main framebuffer of the context. pub fn get_pixel_format(&self) -> PixelFormat { self.pixel_format - }} + } +} unsafe impl Send for EGLContext {} unsafe impl Sync for EGLContext {} @@ -540,8 +577,10 @@ impl Drop for EGLContext { 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 as *const _, self.context as *const _); - self.egl.DestroySurface(self.display as *const _, self.surface as *const _); + self.egl + .DestroyContext(self.display as *const _, self.context as *const _); + self.egl + .DestroySurface(self.display as *const _, self.surface as *const _); self.egl.Terminate(self.display as *const _); } } @@ -569,41 +608,86 @@ pub enum SwapBuffersError { AlreadySwapped, } +/// Attributes to use when creating an OpenGL context. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct GlAttributes { + /// Describes the OpenGL API and version that are being requested when a context is created. + /// + /// `Some(3, 0)` will request a OpenGL ES 3.0 context for example. + /// `None` means "don't care" (minimum will be 2.0). pub version: Option<(u8, u8)>, + /// OpenGL profile to use pub profile: Option, + /// Whether to enable the debug flag of the context. + /// + /// Debug contexts are usually slower but give better error reporting. pub debug: bool, + /// How the OpenGL context should detect errors. 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. pub vsync: bool, } +/// 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, Clone, Copy, PartialEq, Eq)] pub enum Robustness { + /// Not everything is checked. Your application can crash if you do something wrong with your shaders. NotRobust, + /// The driver doesn't check anything. This option is very dangerous. Please know what you're doing before + /// using it. See the GL_KHR_no_error extension. + /// + /// Since this option is purely an optimisation, no error will be returned if the backend doesn't support it. + /// Instead it will automatically fall back to NotRobust. NoError, + /// Everything is checked to avoid any crash. The driver will attempt to avoid any problem, but if a problem occurs + /// the behavior is implementation-defined. You are just guaranteed not to get a crash. RobustNoResetNotification, + /// Same as RobustNoResetNotification but the context creation doesn't fail if it's not supported. TryRobustNoResetNotification, + /// Everything is checked to avoid any crash. If a problem occurs, the context will enter a "context lost" state. + /// It must then be recreated. RobustLoseContextOnReset, + /// Same as RobustLoseContextOnReset but the context creation doesn't fail if it's not supported. TryRobustLoseContextOnReset, } +/// Describes the requested OpenGL context profiles. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum GlProfile { + /// Include all the immediate more functions and definitions. Compatibility, + /// Include all the future-compatible functions and definitions. Core, } +/// Describes how the backend should choose a pixel format. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct PixelFormatRequirements { + /// If `true`, only hardware-accelerated formats will be conisdered. If `false`, only software renderers. + /// `None` means "don't care". Default is `None`. pub hardware_accelerated: Option, + /// Minimum number of bits for the color buffer, excluding alpha. None means "don't care". The default is `None``. pub color_bits: Option, + /// 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. pub float_color_buffer: bool, + /// Minimum number of bits for the alpha in the color buffer. `None` means "don't care". The default is `None`. pub alpha_bits: Option, + /// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`. pub depth_bits: Option, + /// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`. 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 `None`. 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`. 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`. pub stereoscopy: bool, } @@ -653,6 +737,8 @@ pub trait EGLGraphicsBackend: GraphicsBackend { /// Makes the OpenGL context the current context in the current thread. /// + /// # Unsafety + /// /// This function is marked unsafe, because the context cannot be made current /// on multiple threads. unsafe fn make_current(&self) -> Result<(), SwapBuffersError>; diff --git a/src/backend/libinput.rs b/src/backend/libinput.rs index 62d46f5..0f8ab38 100644 --- a/src/backend/libinput.rs +++ b/src/backend/libinput.rs @@ -328,8 +328,8 @@ impl backend::InputBackend for LibinputInputBackend { Entry::Vacant(seat_entry) => { let mut hasher = DefaultHasher::default(); seat_entry.key().hash(&mut hasher); - let seat = seat_entry.insert(backend::Seat::new(hasher.finish(), - new_caps)); + let seat = seat_entry + .insert(backend::Seat::new(hasher.finish(), new_caps)); if let Some(ref mut handler) = self.handler { trace!(self.logger, "Calling on_seat_created with {:?}", seat); handler.on_seat_created(seat); diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 468e969..c95139e 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -1,19 +1,24 @@ +//! Implementation of backend traits for types provided by `winit` + use backend::{SeatInternal, TouchSlotInternal}; use backend::graphics::GraphicsBackend; -use backend::graphics::egl::{CreationError, EGLContext, EGLGraphicsBackend, GlAttributes, Native, Robustness, PixelFormatRequirements, PixelFormat, SwapBuffersError}; +use backend::graphics::egl::{CreationError, EGLContext, EGLGraphicsBackend, GlAttributes, Native, + PixelFormat, PixelFormatRequirements, Robustness, SwapBuffersError}; use backend::input::{Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent, Seat, SeatCapabilities, TouchCancelEvent, TouchDownEvent, TouchMotionEvent, TouchSlot, TouchUpEvent, UnusedEvent}; use nix::c_void; -use wayland_client::egl as wegl; -use winit::{CreationError as WinitCreationError, ElementState, Event, EventsLoop, MouseButton as WinitMouseButton, MouseCursor, MouseScrollDelta, Touch, TouchPhase, Window, WindowBuilder, WindowEvent}; -use winit::os::unix::WindowExt; use std::cmp; use std::error::Error; use std::fmt; use std::rc::Rc; +use wayland_client::egl as wegl; +use winit::{CreationError as WinitCreationError, ElementState, Event, EventsLoop, + MouseButton as WinitMouseButton, MouseCursor, MouseScrollDelta, Touch, TouchPhase, Window, + WindowBuilder, WindowEvent}; +use winit::os::unix::{get_x11_xconnection, WindowExt}; /// Window with an active EGL Context created by `winit`. Implements the /// `EGLGraphicsBackend` graphics backend trait @@ -24,7 +29,7 @@ pub struct WinitGraphicsBackend { /// Abstracted event loop of a `winit` `Window` implementing the `InputBackend` trait /// -/// You need to call `process_new_events` periodically to receive any events. +/// You need to call `dispatch_new_events` periodically to receive any events. pub struct WinitInputBackend { events_loop: EventsLoop, window: Rc, @@ -37,67 +42,78 @@ pub struct WinitInputBackend { } /// Create a new `WinitGraphicsBackend`, which implements the `EGLGraphicsBackend` -/// graphics backend trait +/// graphics backend trait and a corresponding `WinitInputBackend`, which implements +/// the `InputBackend` trait pub fn init() -> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError> { init_from_builder(WindowBuilder::new()) } -pub fn init_from_builder(builder: WindowBuilder) -> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError> { - init_from_builder_with_gl_attr(builder, GlAttributes { - version: None, - profile: None, - debug: cfg!(debug_assertions), - robustness: Robustness::TryRobustLoseContextOnReset, - vsync: true, - }) +/// Create a new `WinitGraphicsBackend`, which implements the `EGLGraphicsBackend` +/// graphics backend trait, from a given `WindowBuilder` struct and a corresponding +/// `WinitInputBackend`, which implements the `InputBackend` trait +pub fn init_from_builder(builder: WindowBuilder) + -> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError> { + init_from_builder_with_gl_attr(builder, + GlAttributes { + version: None, + profile: None, + debug: cfg!(debug_assertions), + robustness: Robustness::TryRobustLoseContextOnReset, + vsync: true, + }) } /// Create a new `WinitGraphicsBackend`, which implements the `EGLGraphicsBackend` -/// graphics backend trait, with a given already configured `WindowBuilder` for -/// customization. -pub fn init_from_builder_with_gl_attr(builder: WindowBuilder, attributes: GlAttributes) -> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError> { +/// graphics backend trait, from a given `WindowBuilder` struct, as well as given +/// `GlAttributes` for further customization of the rendering pipeline and a +/// corresponding `WinitInputBackend`, which implements the `InputBackend` trait. +pub fn init_from_builder_with_gl_attr(builder: WindowBuilder, attributes: GlAttributes) + -> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError> { let events_loop = EventsLoop::new(); let window = Rc::new(builder.build(&events_loop)?); - let (native, surface) = if let (Some(display), Some(window)) = (window.get_xlib_display(), window.get_xlib_window()) { - (Native::X11(display, window), None) - } else if let (Some(display), Some(surface)) = (window.get_wayland_display(), window.get_wayland_client_surface()) { + let (native, surface) = if let (Some(conn), Some(window)) = + (get_x11_xconnection(), window.get_xlib_window()) { + (Native::X11(conn.display as *const _, window), None) + } else if let (Some(display), Some(surface)) = + (window.get_wayland_display(), window.get_wayland_client_surface()) { let (w, h) = window.get_inner_size().unwrap(); let egl_surface = wegl::WlEglSurface::new(surface, w as i32, h as i32); (Native::Wayland(display, egl_surface.ptr() as *const _), Some(egl_surface)) } else { - return Err(CreationError::NotSupported) + return Err(CreationError::NotSupported); }; - let context = unsafe { EGLContext::new(native, attributes, - PixelFormatRequirements { - hardware_accelerated: Some(true), - color_bits: Some(24), - alpha_bits: Some(8), - double_buffer: Some(true), - ..Default::default() - } - )? }; + let context = unsafe { + EGLContext::new(native, + attributes, + PixelFormatRequirements { + hardware_accelerated: Some(true), + color_bits: Some(24), + alpha_bits: Some(8), + ..Default::default() + })? + }; Ok((WinitGraphicsBackend { - window: window.clone(), - context: context, - }, WinitInputBackend { - events_loop: events_loop, - window: window, - surface: surface, - time_counter: 0, - key_counter: 0, - seat: Seat::new(0, - SeatCapabilities { - pointer: true, - keyboard: true, - touch: true, - } - ), - input_config: (), - handler: None, - })) + window: window.clone(), + context: context, + }, + WinitInputBackend { + events_loop: events_loop, + window: window, + surface: surface, + time_counter: 0, + key_counter: 0, + seat: Seat::new(0, + SeatCapabilities { + pointer: true, + keyboard: true, + touch: true, + }), + input_config: (), + handler: None, + })) } impl GraphicsBackend for WinitGraphicsBackend { @@ -122,7 +138,9 @@ impl EGLGraphicsBackend for WinitGraphicsBackend { } fn get_framebuffer_dimensions(&self) -> (u32, u32) { - self.window.get_inner_size_pixels().expect("Window does not exist anymore") + self.window + .get_inner_size_pixels() + .expect("Window does not exist anymore") } fn is_current(&self) -> bool { @@ -143,7 +161,7 @@ impl EGLGraphicsBackend for WinitGraphicsBackend { pub enum WinitInputError { /// The underlying `winit` `Window` was closed. No further events can be processed. /// - /// See `WinitInputBackend::process_new_events`. + /// See `WinitInputBackend::dispatch_new_events`. WindowClosed, } @@ -215,19 +233,13 @@ impl PointerMotionAbsoluteEvent for WinitMouseMovedEvent { fn x_transformed(&self, width: u32) -> u32 { cmp::min(self.x * width as i32 / - self.window - .get_inner_size_points() - .unwrap_or((width, 0)) - .0 as i32, + self.window.get_inner_size_points().unwrap_or((width, 0)).0 as i32, 0) as u32 } fn y_transformed(&self, height: u32) -> u32 { cmp::min(self.y * height as i32 / - self.window - .get_inner_size_points() - .unwrap_or((0, height)) - .1 as i32, + self.window.get_inner_size_points().unwrap_or((0, height)).1 as i32, 0) as u32 } } @@ -322,19 +334,13 @@ impl TouchDownEvent for WinitTouchStartedEvent { fn x_transformed(&self, width: u32) -> u32 { cmp::min(self.location.0 as i32 * width as i32 / - self.window - .get_inner_size_points() - .unwrap_or((width, 0)) - .0 as i32, + self.window.get_inner_size_points().unwrap_or((width, 0)).0 as i32, 0) as u32 } fn y_transformed(&self, height: u32) -> u32 { cmp::min(self.location.1 as i32 * height as i32 / - self.window - .get_inner_size_points() - .unwrap_or((0, height)) - .1 as i32, + self.window.get_inner_size_points().unwrap_or((0, height)).1 as i32, 0) as u32 } } @@ -368,19 +374,11 @@ impl TouchMotionEvent for WinitTouchMovedEvent { } fn x_transformed(&self, width: u32) -> u32 { - self.location.0 as u32 * width / - self.window - .get_inner_size_points() - .unwrap_or((width, 0)) - .0 + self.location.0 as u32 * width / self.window.get_inner_size_points().unwrap_or((width, 0)).0 } fn y_transformed(&self, height: u32) -> u32 { - self.location.1 as u32 * height / - self.window - .get_inner_size_points() - .unwrap_or((0, height)) - .1 + self.location.1 as u32 * height / self.window.get_inner_size_points().unwrap_or((0, height)).1 } } @@ -468,7 +466,7 @@ impl InputBackend for WinitInputBackend { /// input events will be received by a set `InputHandler`. /// /// Returns an error if the `Window` the window has been closed. Calling - /// `process_new_events` again after the `Window` has been closed is considered an + /// `dispatch_new_events` again after the `Window` has been closed is considered an /// application error and unspecified baviour may occur. /// /// The linked `WinitGraphicsBackend` will error with a lost Context and should @@ -485,127 +483,126 @@ impl InputBackend for WinitInputBackend { let surface = &self.surface; let mut handler = self.handler.as_mut(); - self.events_loop.poll_events(move |event| { - if let Some(ref mut handler) = handler { - let Event::WindowEvent{ event, window_id: _ } = event; - match event { - WindowEvent::Resized(x, y) => { - window.set_inner_size(x, y); - if let Some(wl_surface) = surface.as_ref() { - wl_surface.resize(x as i32, y as i32, 0, 0); - } - } - WindowEvent::KeyboardInput(state, key_code, _, _) => { - match state { - ElementState::Pressed => *key_counter += 1, - ElementState::Released => { - *key_counter = key_counter.checked_sub(1).unwrap_or(0) - } - }; - handler.on_keyboard_key(seat, - WinitKeyboardInputEvent { - time: *time_counter, - key: key_code, - count: *key_counter, - state: state, - }) - } - WindowEvent::MouseMoved(x, y) => { - handler.on_pointer_move_absolute(seat, - WinitMouseMovedEvent { - window: window.clone(), + self.events_loop + .poll_events(move |event| if let Some(ref mut handler) = handler { + let Event::WindowEvent { event, .. } = event; + match event { + WindowEvent::Resized(x, y) => { + window.set_inner_size(x, y); + if let Some(wl_surface) = surface.as_ref() { + wl_surface.resize(x as i32, y as i32, 0, 0); + } + } + WindowEvent::KeyboardInput(state, key_code, _, _) => { + match state { + ElementState::Pressed => *key_counter += 1, + ElementState::Released => { + *key_counter = key_counter.checked_sub(1).unwrap_or(0) + } + }; + handler.on_keyboard_key(seat, + WinitKeyboardInputEvent { + time: *time_counter, + key: key_code, + count: *key_counter, + state: state, + }) + } + WindowEvent::MouseMoved(x, y) => { + handler.on_pointer_move_absolute(seat, + WinitMouseMovedEvent { + window: window.clone(), + time: *time_counter, + x: x, + y: y, + }) + } + WindowEvent::MouseWheel(delta, _) => { + let event = WinitMouseWheelEvent { + axis: Axis::Horizontal, + time: *time_counter, + delta: delta, + }; + match delta { + MouseScrollDelta::LineDelta(x, y) | + MouseScrollDelta::PixelDelta(x, y) => { + if x != 0.0 { + handler.on_pointer_axis(seat, event); + } + if y != 0.0 { + handler.on_pointer_axis(seat, event); + } + } + } + } + WindowEvent::MouseInput(state, button) => { + handler.on_pointer_button(seat, + WinitMouseInputEvent { + time: *time_counter, + button: button, + state: state, + }) + } + WindowEvent::Touch(Touch { + phase: TouchPhase::Started, + location: (x, y), + id, + }) => { + handler.on_touch_down(seat, + WinitTouchStartedEvent { + window: window.clone(), + time: *time_counter, + location: (x, y), + id: id, + }) + } + WindowEvent::Touch(Touch { + phase: TouchPhase::Moved, + location: (x, y), + id, + }) => { + handler.on_touch_motion(seat, + WinitTouchMovedEvent { + window: window.clone(), + time: *time_counter, + location: (x, y), + id: id, + }) + } + WindowEvent::Touch(Touch { + phase: TouchPhase::Ended, + location: (x, y), + id, + }) => { + handler.on_touch_motion(seat, + WinitTouchMovedEvent { + window: window.clone(), + time: *time_counter, + location: (x, y), + id: id, + }); + handler.on_touch_up(seat, + WinitTouchEndedEvent { time: *time_counter, - x: x, - y: y, - }) - } - WindowEvent::MouseWheel(delta, _) => { - let event = WinitMouseWheelEvent { - axis: Axis::Horizontal, - time: *time_counter, - delta: delta, - }; - match delta { - MouseScrollDelta::LineDelta(x, y) | - MouseScrollDelta::PixelDelta(x, y) => { - if x != 0.0 { - handler.on_pointer_axis(seat, event.clone()); - } - if y != 0.0 { - handler.on_pointer_axis(seat, event); - } - } - } - } - WindowEvent::MouseInput(state, button) => { - handler.on_pointer_button(seat, - WinitMouseInputEvent { - time: *time_counter, - button: button, - state: state, - }) - } - WindowEvent::Touch(Touch { - phase: TouchPhase::Started, - location: (x, y), - id, - }) => { - handler.on_touch_down(seat, - WinitTouchStartedEvent { - window: window.clone(), - time: *time_counter, - location: (x, y), - id: id, - }) - } - WindowEvent::Touch(Touch { - phase: TouchPhase::Moved, - location: (x, y), - id, - }) => { - handler.on_touch_motion(seat, - WinitTouchMovedEvent { - window: window.clone(), - time: *time_counter, - location: (x, y), - id: id, - }) - } - WindowEvent::Touch(Touch { - phase: TouchPhase::Ended, - location: (x, y), - id, - }) => { - handler.on_touch_motion(seat, - WinitTouchMovedEvent { - window: window.clone(), - time: *time_counter, - location: (x, y), - id: id, - }); - handler.on_touch_up(seat, - WinitTouchEndedEvent { - time: *time_counter, - id: id, - }); - } - WindowEvent::Touch(Touch { - phase: TouchPhase::Cancelled, - id, - .. - }) => { - handler.on_touch_cancel(seat, - WinitTouchCancelledEvent { - time: *time_counter, - id: id, - }) - } - WindowEvent::Closed => *closed_ptr = true, - _ => {} - } - *time_counter += 1; - } - }); + id: id, + }); + } + WindowEvent::Touch(Touch { + phase: TouchPhase::Cancelled, + id, + .. + }) => { + handler.on_touch_cancel(seat, + WinitTouchCancelledEvent { + time: *time_counter, + id: id, + }) + } + WindowEvent::Closed => *closed_ptr = true, + _ => {} + } + *time_counter += 1; + }); } if closed { diff --git a/src/keyboard/mod.rs b/src/keyboard/mod.rs index 475f4f7..883f5e8 100644 --- a/src/keyboard/mod.rs +++ b/src/keyboard/mod.rs @@ -143,7 +143,7 @@ impl KbdInternal { let mods_locked = self.state.serialize_mods(xkb::STATE_MODS_LOCKED); let layout_locked = self.state.serialize_layout(xkb::STATE_LAYOUT_LOCKED); - return (mods_depressed, mods_latched, mods_locked, layout_locked); + (mods_depressed, mods_latched, mods_locked, layout_locked) } fn serialize_pressed_keys(&self) -> Vec { From 786d719dad601bc3e285a8e02119b476904975f4 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sun, 21 May 2017 22:50:50 +0200 Subject: [PATCH 03/13] rustfmt fixes --- src/backend/graphics/egl.rs | 10 ++++------ src/backend/winit.rs | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/backend/graphics/egl.rs b/src/backend/graphics/egl.rs index 0b67345..4e393fa 100644 --- a/src/backend/graphics/egl.rs +++ b/src/backend/graphics/egl.rs @@ -386,8 +386,9 @@ impl EGLContext { let surface = { let surface = match native { - Native::X11(_, window) | Native::Wayland(_, window) | Native::Gbm(_, window) => - egl.CreateWindowSurface(display, config_id, window, ptr::null()), + Native::X11(_, window) | + Native::Wayland(_, window) | + Native::Gbm(_, window) => egl.CreateWindowSurface(display, config_id, window, ptr::null()), }; if surface.is_null() { @@ -399,10 +400,7 @@ impl EGLContext { let mut context_attributes = Vec::with_capacity(10); let mut flags = 0; - if egl_version >= (1, 5) || - extensions - .iter() - .any(|s| s == &"EGL_KHR_create_context") { + if egl_version >= (1, 5) || extensions.iter().any(|s| s == &"EGL_KHR_create_context") { context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32); context_attributes.push(version.0 as i32); context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32); diff --git a/src/backend/winit.rs b/src/backend/winit.rs index c95139e..bbbe4e9 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -18,7 +18,7 @@ use wayland_client::egl as wegl; use winit::{CreationError as WinitCreationError, ElementState, Event, EventsLoop, MouseButton as WinitMouseButton, MouseCursor, MouseScrollDelta, Touch, TouchPhase, Window, WindowBuilder, WindowEvent}; -use winit::os::unix::{get_x11_xconnection, WindowExt}; +use winit::os::unix::{WindowExt, get_x11_xconnection}; /// Window with an active EGL Context created by `winit`. Implements the /// `EGLGraphicsBackend` graphics backend trait From b2f7d6fbfc3fe4c851130905e00cf29b8b8220d8 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Sun, 21 May 2017 22:51:38 +0200 Subject: [PATCH 04/13] rustfmt example --- examples/simple.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/simple.rs b/examples/simple.rs index 709b6d0..18ae991 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,8 +1,8 @@ extern crate wayland_server; extern crate smithay; -use smithay::backend::winit; use smithay::backend::input::InputBackend; +use smithay::backend::winit; use smithay::shm::ShmGlobal; use wayland_server::protocol::wl_shm; From 94d6a0f9873a8efb39402233c60a8d590b02b82c Mon Sep 17 00:00:00 2001 From: Drakulix Date: Tue, 23 May 2017 11:02:39 +0200 Subject: [PATCH 05/13] Fix broken robost implementation --- src/backend/winit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/winit.rs b/src/backend/winit.rs index bbbe4e9..450bee9 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -58,7 +58,7 @@ pub fn init_from_builder(builder: WindowBuilder) version: None, profile: None, debug: cfg!(debug_assertions), - robustness: Robustness::TryRobustLoseContextOnReset, + robustness: Robustness::NotRobust, vsync: true, }) } From dbaf4f774682dac9ccd15e8ce70ce31acdff9cb2 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Tue, 23 May 2017 11:03:03 +0200 Subject: [PATCH 06/13] Rename variable to avoid confusion --- src/backend/winit.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 450bee9..5ecfdff 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -489,8 +489,8 @@ impl InputBackend for WinitInputBackend { match event { WindowEvent::Resized(x, y) => { window.set_inner_size(x, y); - if let Some(wl_surface) = surface.as_ref() { - wl_surface.resize(x as i32, y as i32, 0, 0); + if let Some(wl_egl_surface) = surface.as_ref() { + wl_egl_surface.resize(x as i32, y as i32, 0, 0); } } WindowEvent::KeyboardInput(state, key_code, _, _) => { From 115eb4d2003a5b2f91930c53c5376c6986e0c780 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Fri, 2 Jun 2017 13:15:31 +0200 Subject: [PATCH 07/13] Update winit --- Cargo.toml | 2 +- src/backend/winit.rs | 279 +++++++++++++++++++++++-------------------- 2 files changed, 152 insertions(+), 129 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4ff16bb..f78f43a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ slog = { version = "2.0.0" } slog-stdlog = "2.0.0-0.2" libloading = "0.4.0" wayland-client = { version = "~0.8.6", optional = true } -winit = { version = "~0.6.4", optional = true } +winit = { git = "https://github.com/tomaka/winit.git", optional = true } glium = { version = "~0.16.0", optional = true } input = { version = "~0.1.1", optional = true } clippy = { version = "*", optional = true } diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 5ecfdff..aee4c12 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -15,7 +15,7 @@ use std::error::Error; use std::fmt; use std::rc::Rc; use wayland_client::egl as wegl; -use winit::{CreationError as WinitCreationError, ElementState, Event, EventsLoop, +use winit::{CreationError as WinitCreationError, ElementState, Event, EventsLoop, KeyboardInput, MouseButton as WinitMouseButton, MouseCursor, MouseScrollDelta, Touch, TouchPhase, Window, WindowBuilder, WindowEvent}; use winit::os::unix::{WindowExt, get_x11_xconnection}; @@ -45,7 +45,10 @@ pub struct WinitInputBackend { /// graphics backend trait and a corresponding `WinitInputBackend`, which implements /// the `InputBackend` trait pub fn init() -> Result<(WinitGraphicsBackend, WinitInputBackend), CreationError> { - init_from_builder(WindowBuilder::new()) + init_from_builder(WindowBuilder::new() + .with_dimensions(1280, 800) + .with_title("Smithay") + .with_visibility(true)) } /// Create a new `WinitGraphicsBackend`, which implements the `EGLGraphicsBackend` @@ -85,14 +88,20 @@ pub fn init_from_builder_with_gl_attr(builder: WindowBuilder, attributes: GlAttr }; let context = unsafe { - EGLContext::new(native, - attributes, - PixelFormatRequirements { - hardware_accelerated: Some(true), - color_bits: Some(24), - alpha_bits: Some(8), - ..Default::default() - })? + match EGLContext::new(native, + attributes, + PixelFormatRequirements { + hardware_accelerated: Some(true), + color_bits: Some(24), + alpha_bits: Some(8), + ..Default::default() + }) { + Ok(context) => context, + Err(err) => { + println!("EGLContext creation failed:\n{}", err); + return Err(err); + } + } }; Ok((WinitGraphicsBackend { @@ -182,7 +191,7 @@ impl fmt::Display for WinitInputError { /// Winit-Backend internal event wrapping winit's types into a `KeyboardKeyEvent` pub struct WinitKeyboardInputEvent { time: u32, - key: u8, + key: u32, count: u32, state: ElementState, } @@ -195,7 +204,7 @@ impl BackendEvent for WinitKeyboardInputEvent { impl KeyboardKeyEvent for WinitKeyboardInputEvent { fn key_code(&self) -> u32 { - self.key as u32 + self.key } fn state(&self) -> KeyState { @@ -212,8 +221,8 @@ impl KeyboardKeyEvent for WinitKeyboardInputEvent { pub struct WinitMouseMovedEvent { window: Rc, time: u32, - x: i32, - y: i32, + x: f64, + y: f64, } impl BackendEvent for WinitMouseMovedEvent { @@ -224,23 +233,24 @@ impl BackendEvent for WinitMouseMovedEvent { impl PointerMotionAbsoluteEvent for WinitMouseMovedEvent { fn x(&self) -> f64 { - self.x as f64 + self.x } fn y(&self) -> f64 { - self.y as f64 + self.y } fn x_transformed(&self, width: u32) -> u32 { - cmp::min(self.x * width as i32 / - self.window.get_inner_size_points().unwrap_or((width, 0)).0 as i32, - 0) as u32 + cmp::min((self.x * width as f64 / + self.window.get_inner_size_points().unwrap_or((width, 0)).0 as f64) as u32, + 0) } fn y_transformed(&self, height: u32) -> u32 { - cmp::min(self.y * height as i32 / - self.window.get_inner_size_points().unwrap_or((0, height)).1 as i32, - 0) as u32 + cmp::min((self.y * height as f64 / + self.window.get_inner_size_points().unwrap_or((0, height)).1 as f64) as + u32, + 0) } } @@ -484,124 +494,137 @@ impl InputBackend for WinitInputBackend { let mut handler = self.handler.as_mut(); self.events_loop - .poll_events(move |event| if let Some(ref mut handler) = handler { - let Event::WindowEvent { event, .. } = event; - match event { - WindowEvent::Resized(x, y) => { - window.set_inner_size(x, y); - if let Some(wl_egl_surface) = surface.as_ref() { - wl_egl_surface.resize(x as i32, y as i32, 0, 0); - } - } - WindowEvent::KeyboardInput(state, key_code, _, _) => { - match state { - ElementState::Pressed => *key_counter += 1, - ElementState::Released => { - *key_counter = key_counter.checked_sub(1).unwrap_or(0) + .poll_events(move |event| match event { + Event::WindowEvent { event, .. } => { + match (event, handler.as_mut()) { + (WindowEvent::Resized(x, y), _) => { + window.set_inner_size(x, y); + if let Some(wl_egl_surface) = surface.as_ref() { + wl_egl_surface.resize(x as i32, y as i32, 0, 0); } - }; - handler.on_keyboard_key(seat, - WinitKeyboardInputEvent { - time: *time_counter, - key: key_code, - count: *key_counter, - state: state, - }) - } - WindowEvent::MouseMoved(x, y) => { - handler.on_pointer_move_absolute(seat, - WinitMouseMovedEvent { - window: window.clone(), - time: *time_counter, - x: x, - y: y, - }) - } - WindowEvent::MouseWheel(delta, _) => { - let event = WinitMouseWheelEvent { - axis: Axis::Horizontal, - time: *time_counter, - delta: delta, - }; - match delta { - MouseScrollDelta::LineDelta(x, y) | - MouseScrollDelta::PixelDelta(x, y) => { - if x != 0.0 { - handler.on_pointer_axis(seat, event); + } + (WindowEvent::KeyboardInput { + input: KeyboardInput { scancode, state, .. }, .. + }, + Some(handler)) => { + match state { + ElementState::Pressed => *key_counter += 1, + ElementState::Released => { + *key_counter = key_counter.checked_sub(1).unwrap_or(0) } - if y != 0.0 { - handler.on_pointer_axis(seat, event); + }; + handler.on_keyboard_key(seat, + WinitKeyboardInputEvent { + time: *time_counter, + key: scancode, + count: *key_counter, + state: state, + }) + } + (WindowEvent::MouseMoved { position: (x, y), .. }, + Some(handler)) => { + handler.on_pointer_move_absolute(seat, + WinitMouseMovedEvent { + window: window.clone(), + time: *time_counter, + x: x, + y: y, + }) + } + (WindowEvent::MouseWheel { delta, .. }, Some(handler)) => { + let event = WinitMouseWheelEvent { + axis: Axis::Horizontal, + time: *time_counter, + delta: delta, + }; + match delta { + MouseScrollDelta::LineDelta(x, y) | + MouseScrollDelta::PixelDelta(x, y) => { + if x != 0.0 { + handler.on_pointer_axis(seat, event); + } + if y != 0.0 { + handler.on_pointer_axis(seat, event); + } } } } - } - WindowEvent::MouseInput(state, button) => { - handler.on_pointer_button(seat, - WinitMouseInputEvent { + (WindowEvent::MouseInput { state, button, .. }, Some(handler)) => { + handler.on_pointer_button(seat, + WinitMouseInputEvent { + time: *time_counter, + button: button, + state: state, + }) + } + (WindowEvent::Touch(Touch { + phase: TouchPhase::Started, + location: (x, y), + id, + .. + }), + Some(handler)) => { + handler.on_touch_down(seat, + WinitTouchStartedEvent { + window: window.clone(), time: *time_counter, - button: button, - state: state, + location: (x, y), + id: id, }) - } - WindowEvent::Touch(Touch { - phase: TouchPhase::Started, - location: (x, y), - id, - }) => { - handler.on_touch_down(seat, - WinitTouchStartedEvent { - window: window.clone(), - time: *time_counter, - location: (x, y), - id: id, - }) - } - WindowEvent::Touch(Touch { - phase: TouchPhase::Moved, - location: (x, y), - id, - }) => { - handler.on_touch_motion(seat, - WinitTouchMovedEvent { - window: window.clone(), + } + (WindowEvent::Touch(Touch { + phase: TouchPhase::Moved, + location: (x, y), + id, + .. + }), + Some(handler)) => { + handler.on_touch_motion(seat, + WinitTouchMovedEvent { + window: window.clone(), + time: *time_counter, + location: (x, y), + id: id, + }) + } + (WindowEvent::Touch(Touch { + phase: TouchPhase::Ended, + location: (x, y), + id, + .. + }), + Some(handler)) => { + handler.on_touch_motion(seat, + WinitTouchMovedEvent { + window: window.clone(), + time: *time_counter, + location: (x, y), + id: id, + }); + handler.on_touch_up(seat, + WinitTouchEndedEvent { time: *time_counter, - location: (x, y), - id: id, - }) - } - WindowEvent::Touch(Touch { - phase: TouchPhase::Ended, - location: (x, y), - id, - }) => { - handler.on_touch_motion(seat, - WinitTouchMovedEvent { - window: window.clone(), - time: *time_counter, - location: (x, y), id: id, }); - handler.on_touch_up(seat, - WinitTouchEndedEvent { - time: *time_counter, - id: id, - }); + } + (WindowEvent::Touch(Touch { + phase: TouchPhase::Cancelled, + id, + .. + }), + Some(handler)) => { + handler.on_touch_cancel(seat, + WinitTouchCancelledEvent { + time: *time_counter, + id: id, + }) + } + (WindowEvent::Closed, _) => *closed_ptr = true, + _ => {} } - WindowEvent::Touch(Touch { - phase: TouchPhase::Cancelled, - id, - .. - }) => { - handler.on_touch_cancel(seat, - WinitTouchCancelledEvent { - time: *time_counter, - id: id, - }) - } - WindowEvent::Closed => *closed_ptr = true, - _ => {} + *time_counter += 1; } - *time_counter += 1; + Event::DeviceEvent { .. } => {} }); } From 2c9f6a7479e0adb3ef36458a96e74467acafd5a3 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Fri, 2 Jun 2017 13:16:12 +0200 Subject: [PATCH 08/13] Flesh out glium compatibility --- src/backend/glium.rs | 70 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/src/backend/glium.rs b/src/backend/glium.rs index 26eefd9..2105eed 100644 --- a/src/backend/glium.rs +++ b/src/backend/glium.rs @@ -1,9 +1,15 @@ -use backend::graphics::egl::{EGLGraphicsBackend, SwapBuffersError}; -use glium::SwapBuffersError as GliumSwapBuffersError; -use glium::backend::Backend; +//! Glium compatibility module +use backend::graphics::egl::{EGLGraphicsBackend, SwapBuffersError}; +use glium::Frame; +use glium::SwapBuffersError as GliumSwapBuffersError; +use glium::backend::{Backend, Context}; +use glium::debug::DebugCallbackBehavior; +use std::ops::Deref; use std::os::raw::c_void; +use std::rc::Rc; + impl From for GliumSwapBuffersError { fn from(error: SwapBuffersError) -> Self { match error { @@ -13,19 +19,61 @@ impl From for GliumSwapBuffersError { } } -pub struct GliumGraphicBackend(T); - -pub trait IntoGlium: EGLGraphicsBackend + Sized { - fn into_glium(self) -> GliumGraphicBackend; +/// Wrapper to expose `glium` compatibility +pub struct GliumGraphicsBackend { + context: Rc, + backend: Rc>, } -impl IntoGlium for T { - fn into_glium(self) -> GliumGraphicBackend { - GliumGraphicBackend(self) +struct InternalBackend(T); + +impl GliumGraphicsBackend { + fn new(backend: T) -> GliumGraphicsBackend { + let internal = Rc::new(InternalBackend(backend)); + + GliumGraphicsBackend { + // cannot fail + context: unsafe { + Context::new::<_, ()>(internal.clone(), false, DebugCallbackBehavior::default()).unwrap() + }, + backend: internal, + } + } + + /// Start drawing on the backbuffer. + /// + /// This function returns a `Frame`, which can be used to draw on it. When the `Frame` is + /// destroyed, the buffers are swapped. + /// + /// Note that destroying a `Frame` is immediate, even if vsync is enabled. + #[inline] + pub fn draw(&self) -> Frame { + Frame::new(self.context.clone(), + self.backend.get_framebuffer_dimensions()) } } -unsafe impl Backend for GliumGraphicBackend { +impl Deref for GliumGraphicsBackend { + type Target = Context; + + fn deref(&self) -> &Context { + &self.context + } +} + +/// Converter trait to expose `glium` compatibility for all `EGLGraphicsBackend`s +pub trait IntoGlium: EGLGraphicsBackend + Sized { + /// Wrap the given `EGLGraphicsBackend` to a `GliumGraphicBackend` + fn into_glium(self) -> GliumGraphicsBackend; +} + +impl IntoGlium for T { + fn into_glium(self) -> GliumGraphicsBackend { + GliumGraphicsBackend::new(self) + } +} + +unsafe impl Backend for InternalBackend { fn swap_buffers(&self) -> Result<(), GliumSwapBuffersError> { self.0.swap_buffers().map_err(Into::into) } From e191c08186ad784725727b00bd921dc7feadfadb Mon Sep 17 00:00:00 2001 From: Drakulix Date: Fri, 2 Jun 2017 13:17:06 +0200 Subject: [PATCH 09/13] Move glium.rs --- src/backend/{ => graphics}/glium.rs | 0 src/backend/graphics/mod.rs | 2 ++ src/backend/mod.rs | 7 +------ 3 files changed, 3 insertions(+), 6 deletions(-) rename src/backend/{ => graphics}/glium.rs (100%) diff --git a/src/backend/glium.rs b/src/backend/graphics/glium.rs similarity index 100% rename from src/backend/glium.rs rename to src/backend/graphics/glium.rs diff --git a/src/backend/graphics/mod.rs b/src/backend/graphics/mod.rs index 35855a2..cb04c21 100644 --- a/src/backend/graphics/mod.rs +++ b/src/backend/graphics/mod.rs @@ -31,3 +31,5 @@ pub trait GraphicsBackend { pub mod software; pub mod egl; +#[cfg(feature = "renderer_glium")] +pub mod glium; diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 3d1ef0a..1102635 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -21,12 +21,7 @@ pub mod winit; #[cfg(feature = "backend_libinput")] pub mod libinput; -#[cfg(feature = "renderer_glium")] -mod glium; -#[cfg(feature = "renderer_glium")] -pub use glium::*; - -/// Internal functions that need to be accessible by the different backend implementations +// Internal functions that need to be accessible by the different backend implementations trait SeatInternal { fn new(id: u64, capabilities: input::SeatCapabilities) -> Self; From f890b4011d0daa964a1b43872decebc3ecc89be7 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Fri, 2 Jun 2017 15:25:16 +0200 Subject: [PATCH 10/13] Fix egl initialization segfaults - Don't initialize a surface twice, if context creation fails for one version - Don't let the loaded egl library go out of scope and thus invalidating the function pointers --- src/backend/graphics/egl.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/backend/graphics/egl.rs b/src/backend/graphics/egl.rs index 4e393fa..4a37441 100644 --- a/src/backend/graphics/egl.rs +++ b/src/backend/graphics/egl.rs @@ -117,10 +117,11 @@ impl error::Error for CreationError { /// EGL context for rendering pub struct EGLContext { - context: *const c_void, - display: *const c_void, + _lib: Library, + context: ffi::egl::types::EGLContext, + display: ffi::egl::types::EGLDisplay, egl: ffi::egl::Egl, - surface: *const c_void, + surface: ffi::egl::types::EGLSurface, pixel_format: PixelFormat, } @@ -384,19 +385,6 @@ impl EGLContext { srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that }; - let surface = { - let surface = match native { - Native::X11(_, window) | - Native::Wayland(_, window) | - Native::Gbm(_, window) => egl.CreateWindowSurface(display, config_id, window, ptr::null()), - }; - - if surface.is_null() { - return Err(CreationError::OsError(String::from("eglCreateWindowSurface failed"))); - } - surface - }; - let mut context_attributes = Vec::with_capacity(10); let mut flags = 0; @@ -498,7 +486,21 @@ impl EGLContext { } } + let surface = { + let surface = match native { + Native::X11(_, window) | + Native::Wayland(_, window) | + Native::Gbm(_, window) => egl.CreateWindowSurface(display, config_id, window, ptr::null()), + }; + + if surface.is_null() { + return Err(CreationError::OsError(String::from("eglCreateWindowSurface failed"))); + } + surface + }; + Ok(EGLContext { + _lib: lib, context: context as *const _, display: display as *const _, egl: egl, From ca5076a4530852ab7c13b0b7645e6cb1bd34c693 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Fri, 2 Jun 2017 15:26:50 +0200 Subject: [PATCH 11/13] Provide a little more fleshed out and working example --- examples/simple.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index 18ae991..c9df850 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,6 +1,9 @@ extern crate wayland_server; extern crate smithay; +extern crate glium; +use glium::Surface; +use smithay::backend::graphics::glium::IntoGlium; use smithay::backend::input::InputBackend; use smithay::backend::winit; use smithay::shm::ShmGlobal; @@ -8,15 +11,13 @@ use wayland_server::protocol::wl_shm; fn main() { // Initialize a simple backend for testing - let (mut renderer, mut input) = winit::init().unwrap(); + let (renderer, mut input) = winit::init().unwrap(); - let (_, mut event_loop) = wayland_server::create_display(); + let (_display, mut event_loop) = wayland_server::create_display(); // Insert the ShmGlobal as a handler to your event loop // Here, we specify tha the standard Argb8888 and Xrgb8888 is the only supported. - let handler_id = - event_loop.add_handler_with_init(ShmGlobal::new(vec![], - None /* we don't provide a logger here */)); + let handler_id = event_loop.add_handler_with_init(ShmGlobal::new(vec![], None /* we don't provide a logger here */)); // Register this handler to advertise a wl_shm global of version 1 let shm_global = event_loop.register_global::(handler_id, 1); @@ -27,10 +28,17 @@ fn main() { state.get_handler::(handler_id).get_token() }; - // TODO render stuff + // Init glium + let context = renderer.into_glium(); - // TODO put input handling on the event loop - input.dispatch_new_events().unwrap(); - event_loop.run().unwrap(); + loop { + input.dispatch_new_events().unwrap(); + + let mut frame = context.draw(); + frame.clear(None, Some((0.0, 0.0, 0.0, 1.0)), false, None, None); + frame.finish().unwrap(); + + event_loop.dispatch(Some(16)).unwrap(); + } } From a7628610d4e7cea27dbed0fea4778ed541aa656f Mon Sep 17 00:00:00 2001 From: Drakulix Date: Fri, 2 Jun 2017 15:32:31 +0200 Subject: [PATCH 12/13] Remove robustness for now --- src/backend/graphics/egl.rs | 103 +----------------------------------- src/backend/winit.rs | 3 +- 2 files changed, 2 insertions(+), 104 deletions(-) diff --git a/src/backend/graphics/egl.rs b/src/backend/graphics/egl.rs index 4a37441..50c934e 100644 --- a/src/backend/graphics/egl.rs +++ b/src/backend/graphics/egl.rs @@ -60,8 +60,6 @@ pub enum CreationError { IoError(io::Error), /// Operating System error OsError(String), - /// Robustness was requested but is not supported by the graphics system - RobustnessNotSupported, /// The requested OpenGl version is not supported by the graphics system OpenGlVersionNotSupported, /// There is no pixel format available that fulfills all requirements @@ -91,10 +89,6 @@ impl error::Error for CreationError { match *self { CreationError::IoError(ref err) => err.description(), CreationError::OsError(ref text) => text, - CreationError::RobustnessNotSupported => { - "You requested robustness, but it is \ - not supported." - } CreationError::OpenGlVersionNotSupported => { "The requested OpenGL version is not \ supported." @@ -386,7 +380,6 @@ impl EGLContext { }; let mut context_attributes = Vec::with_capacity(10); - let mut flags = 0; if egl_version >= (1, 5) || extensions.iter().any(|s| s == &"EGL_KHR_create_context") { context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32); @@ -394,83 +387,15 @@ impl EGLContext { context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32); context_attributes.push(version.1 as i32); - // handling robustness - let supports_robustness = egl_version >= (1, 5) || - extensions - .iter() - .any(|s| s == "EGL_EXT_create_context_robustness"); - - match attributes.robustness { - Robustness::NotRobust => (), - - Robustness::NoError => { - if extensions - .iter() - .any(|s| s == "EGL_KHR_create_context_no_error") { - 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 |= 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 |= 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 |= 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 |= ffi::egl::CONTEXT_OPENGL_ROBUST_ACCESS as c_int; - } - } - } - if attributes.debug && egl_version >= (1, 5) { context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32); context_attributes.push(ffi::egl::TRUE as i32); } context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32); - context_attributes.push(flags); + context_attributes.push(0); } else if egl_version >= (1, 3) { - // robustness is not supported - match attributes.robustness { - Robustness::RobustNoResetNotification | - Robustness::RobustLoseContextOnReset => { - return Err(CreationError::RobustnessNotSupported); - } - _ => (), - } - context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); context_attributes.push(version.0 as i32); } @@ -622,37 +547,11 @@ pub struct GlAttributes { /// /// Debug contexts are usually slower but give better error reporting. pub debug: bool, - /// How the OpenGL context should detect errors. - 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. pub vsync: bool, } -/// 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, Clone, Copy, PartialEq, Eq)] -pub enum Robustness { - /// Not everything is checked. Your application can crash if you do something wrong with your shaders. - NotRobust, - /// The driver doesn't check anything. This option is very dangerous. Please know what you're doing before - /// using it. See the GL_KHR_no_error extension. - /// - /// Since this option is purely an optimisation, no error will be returned if the backend doesn't support it. - /// Instead it will automatically fall back to NotRobust. - NoError, - /// Everything is checked to avoid any crash. The driver will attempt to avoid any problem, but if a problem occurs - /// the behavior is implementation-defined. You are just guaranteed not to get a crash. - RobustNoResetNotification, - /// Same as RobustNoResetNotification but the context creation doesn't fail if it's not supported. - TryRobustNoResetNotification, - /// Everything is checked to avoid any crash. If a problem occurs, the context will enter a "context lost" state. - /// It must then be recreated. - RobustLoseContextOnReset, - /// Same as RobustLoseContextOnReset but the context creation doesn't fail if it's not supported. - TryRobustLoseContextOnReset, -} - /// Describes the requested OpenGL context profiles. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum GlProfile { diff --git a/src/backend/winit.rs b/src/backend/winit.rs index aee4c12..e5cfcef 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -3,7 +3,7 @@ use backend::{SeatInternal, TouchSlotInternal}; use backend::graphics::GraphicsBackend; use backend::graphics::egl::{CreationError, EGLContext, EGLGraphicsBackend, GlAttributes, Native, - PixelFormat, PixelFormatRequirements, Robustness, SwapBuffersError}; + PixelFormat, PixelFormatRequirements, SwapBuffersError}; use backend::input::{Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent, Seat, SeatCapabilities, TouchCancelEvent, TouchDownEvent, @@ -61,7 +61,6 @@ pub fn init_from_builder(builder: WindowBuilder) version: None, profile: None, debug: cfg!(debug_assertions), - robustness: Robustness::NotRobust, vsync: true, }) } From e28b237c16c4458c7d021cde2bd76c76a353bf96 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Fri, 2 Jun 2017 16:21:29 +0200 Subject: [PATCH 13/13] Fix example formatting --- examples/simple.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/simple.rs b/examples/simple.rs index c9df850..8ed83c9 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -17,7 +17,9 @@ fn main() { // Insert the ShmGlobal as a handler to your event loop // Here, we specify tha the standard Argb8888 and Xrgb8888 is the only supported. - let handler_id = event_loop.add_handler_with_init(ShmGlobal::new(vec![], None /* we don't provide a logger here */)); + let handler_id = + event_loop.add_handler_with_init(ShmGlobal::new(vec![], + None /* we don't provide a logger here */)); // Register this handler to advertise a wl_shm global of version 1 let shm_global = event_loop.register_global::(handler_id, 1);