diff --git a/anvil/Cargo.toml b/anvil/Cargo.toml index 2c2fb76..d321b5b 100644 --- a/anvil/Cargo.toml +++ b/anvil/Cargo.toml @@ -14,12 +14,14 @@ rand = "0.7" slog = { version = "2.1.1" } slog-term = "2.8" slog-async = "2.2" +slog-stdlog = "4.1.0" +slog-scope = "4.4.0" xkbcommon = "0.4.0" [dependencies.smithay] path = ".." default-features = false -features = [ "renderer_gl", "backend_egl", "wayland_frontend" ] +features = [ "renderer_gl", "backend_egl", "wayland_frontend", "slog-stdlog" ] [dependencies.x11rb] optional = true diff --git a/anvil/src/main.rs b/anvil/src/main.rs index 257ccab..46183ca 100644 --- a/anvil/src/main.rs +++ b/anvil/src/main.rs @@ -44,6 +44,8 @@ fn main() { //std::sync::Mutex::new(slog_term::term_full().fuse()).fuse(), o!(), ); + let _guard = slog_scope::set_global_logger(log.clone()); + slog_stdlog::init().expect("Could not setup log backend"); let arg = ::std::env::args().nth(1); match arg.as_ref().map(|s| &s[..]) { diff --git a/src/backend/egl/display.rs b/src/backend/egl/display.rs index 44a649a..8ed4074 100644 --- a/src/backend/egl/display.rs +++ b/src/backend/egl/display.rs @@ -75,24 +75,9 @@ pub struct EGLDisplay { fn select_platform_display( native: &N, + dp_extensions: &[String], log: &::slog::Logger, ) -> Result<*const c_void, Error> { - let dp_extensions = unsafe { - let p = wrap_egl_call(|| ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32)) - .map_err(Error::InitFailed)?; //TODO EGL_EXT_client_extensions not supported - - // this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise - // `eglQueryString` returns an error - if p.is_null() { - return Err(Error::EglExtensionNotSupported(&["EGL_EXT_platform_base"])); - } else { - let p = CStr::from_ptr(p); - let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new()); - list.split(' ').map(|e| e.to_string()).collect::>() - } - }; - debug!(log, "Supported EGL client extensions: {:?}", dp_extensions); - for platform in native.supported_platforms() { debug!(log, "Trying EGL platform: {}", platform.platform_name); @@ -160,10 +145,11 @@ impl EGLDisplay { L: Into>, { let log = crate::slog_or_fallback(logger.into()).new(o!("smithay_module" => "backend_egl")); - ffi::make_sure_egl_is_loaded(); + let dp_extensions = ffi::make_sure_egl_is_loaded()?; + debug!(log, "Supported EGL client extensions: {:?}", dp_extensions); // we create an EGLDisplay - let display = select_platform_display(native, &log)?; + let display = select_platform_display(native, &dp_extensions, &log)?; // We can then query the egl api version let egl_version = { diff --git a/src/backend/egl/error.rs b/src/backend/egl/error.rs index 6690e8f..5ff1843 100644 --- a/src/backend/egl/error.rs +++ b/src/backend/egl/error.rs @@ -127,7 +127,7 @@ impl From for EGLError { } impl EGLError { - fn from_last_call() -> Result<(), EGLError> { + pub(super) fn from_last_call() -> Result<(), EGLError> { match unsafe { ffi::egl::GetError() as u32 } { ffi::egl::SUCCESS => Ok(()), x => Err(EGLError::from(x)), diff --git a/src/backend/egl/ffi.rs b/src/backend/egl/ffi.rs index caa7c94..e40867c 100644 --- a/src/backend/egl/ffi.rs +++ b/src/backend/egl/ffi.rs @@ -1,11 +1,14 @@ #![allow(missing_docs)] +use super::Error; use nix::libc::{c_long, c_uint, c_void}; pub type khronos_utime_nanoseconds_t = khronos_uint64_t; pub type khronos_uint64_t = u64; pub type khronos_ssize_t = c_long; pub type EGLint = i32; +pub type EGLchar = char; +pub type EGLLabelKHR = *const c_void; pub type EGLNativeDisplayType = NativeDisplayType; pub type EGLNativePixmapType = NativePixmapType; pub type EGLNativeWindowType = NativeWindowType; @@ -13,17 +16,46 @@ pub type NativeDisplayType = *const c_void; pub type NativePixmapType = *const c_void; pub type NativeWindowType = *const c_void; -pub fn make_sure_egl_is_loaded() { - use std::{ffi::CString, ptr}; +extern "system" fn egl_debug_log( + severity: egl::types::EGLenum, + command: *const EGLchar, + _id: EGLint, + _thread: EGLLabelKHR, + _obj: EGLLabelKHR, + message: *const EGLchar, +) { + let _ = std::panic::catch_unwind(move || unsafe { + let msg = std::ffi::CStr::from_ptr(message as *const _); + let message_utf8 = msg.to_string_lossy(); + let cmd = std::ffi::CStr::from_ptr(command as *const _); + let command_utf8 = cmd.to_string_lossy(); + let logger = crate::slog_or_fallback(None).new(slog::o!("backend" => "egl")); + match severity { + egl::DEBUG_MSG_CRITICAL_KHR | egl::DEBUG_MSG_ERROR_KHR => { + slog::error!(logger, "[EGL] {}: {}", command_utf8, message_utf8) + } + egl::DEBUG_MSG_WARN_KHR => slog::warn!(logger, "[EGL] {}: {}", command_utf8, message_utf8), + egl::DEBUG_MSG_INFO_KHR => slog::info!(logger, "[EGL] {}: {}", command_utf8, message_utf8), + _ => slog::debug!(logger, "[EGL] {}: {}", command_utf8, message_utf8), + }; + }); +} + +pub fn make_sure_egl_is_loaded() -> Result, Error> { + use std::{ + ffi::{CStr, CString}, + ptr, + }; + + fn constrain(f: F) -> F + where + F: for<'a> Fn(&'a str) -> *const ::std::os::raw::c_void, + { + f + } + let proc_address = constrain(|sym| unsafe { super::get_proc_address(sym) }); egl::LOAD.call_once(|| unsafe { - fn constrain(f: F) -> F - where - F: for<'a> Fn(&'a str) -> *const ::std::os::raw::c_void, - { - f - } - egl::load_with(|sym| { let name = CString::new(sym).unwrap(); let symbol = egl::LIB.get::<*mut c_void>(name.as_bytes()); @@ -32,14 +64,47 @@ pub fn make_sure_egl_is_loaded() { Err(_) => ptr::null(), } }); - let proc_address = constrain(|sym| super::get_proc_address(sym)); egl::load_with(&proc_address); egl::BindWaylandDisplayWL::load_with(&proc_address); egl::UnbindWaylandDisplayWL::load_with(&proc_address); egl::QueryWaylandBufferWL::load_with(&proc_address); - #[cfg(feature = "backend_drm_eglstream")] - egl::StreamConsumerAcquireAttribNV::load_with(&proc_address); + egl::DebugMessageControlKHR::load_with(&proc_address); }); + + let extensions = unsafe { + let p = super::wrap_egl_call(|| egl::QueryString(egl::NO_DISPLAY, egl::EXTENSIONS as i32)) + .map_err(Error::InitFailed)?; //TODO EGL_EXT_client_extensions not supported + + // this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise + // `eglQueryString` returns an error + if p.is_null() { + return Err(Error::EglExtensionNotSupported(&["EGL_EXT_platform_base"])); + } else { + let p = CStr::from_ptr(p); + let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new()); + list.split(' ').map(|e| e.to_string()).collect::>() + } + }; + + egl::DEBUG.call_once(|| unsafe { + if extensions.iter().any(|ext| ext == "EGL_KHR_debug") { + let debug_attribs = [ + egl::DEBUG_MSG_CRITICAL_KHR as isize, + egl::TRUE as isize, + egl::DEBUG_MSG_ERROR_KHR as isize, + egl::TRUE as isize, + egl::DEBUG_MSG_WARN_KHR as isize, + egl::TRUE as isize, + egl::DEBUG_MSG_INFO_KHR as isize, + egl::TRUE as isize, + egl::NONE as isize, + ]; + // we do not check for success, because there is not much we can do otherwise. + egl::DebugMessageControlKHR(Some(egl_debug_log), debug_attribs.as_ptr()); + } + }); + + Ok(extensions) } #[allow(clippy::all, missing_debug_implementations)] @@ -53,9 +118,40 @@ pub mod egl { } pub static LOAD: Once = Once::new(); + pub static DEBUG: Once = Once::new(); include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs")); + type EGLDEBUGPROCKHR = Option< + extern "system" fn( + _error: egl::types::EGLenum, + command: *const EGLchar, + _id: EGLint, + _thread: EGLLabelKHR, + _obj: EGLLabelKHR, + message: *const EGLchar, + ), + >; + #[allow(dead_code, non_upper_case_globals)] + pub const DEBUG_MSG_CRITICAL_KHR: types::EGLenum = 0x33B9; + #[allow(dead_code, non_upper_case_globals)] + pub const DEBUG_MSG_ERROR_KHR: types::EGLenum = 0x33BA; + #[allow(dead_code, non_upper_case_globals)] + pub const DEBUG_MSG_INFO_KHR: types::EGLenum = 0x33BC; + #[allow(dead_code, non_upper_case_globals)] + pub const DEBUG_MSG_WARN_KHR: types::EGLenum = 0x33BB; + + #[allow(non_snake_case, unused_variables, dead_code)] + #[inline] + pub unsafe fn DebugMessageControlKHR( + callback: EGLDEBUGPROCKHR, + attrib_list: *const types::EGLAttrib, + ) -> types::EGLint { + __gl_imports::mem::transmute::< + _, + extern "system" fn(EGLDEBUGPROCKHR, *const types::EGLAttrib) -> types::EGLint, + >(wayland_storage::DebugMessageControlKHR.f)(callback, attrib_list) + } /* * `gl_generator` cannot generate bindings for the `EGL_WL_bind_wayland_display` extension. * Lets do it ourselves... @@ -118,6 +214,34 @@ pub mod egl { f: super::missing_fn_panic as *const raw::c_void, is_loaded: false, }; + pub static mut DebugMessageControlKHR: FnPtr = FnPtr { + f: super::missing_fn_panic as *const raw::c_void, + is_loaded: false, + }; + } + + #[allow(non_snake_case)] + pub mod DebugMessageControlKHR { + use super::FnPtr; + use super::__gl_imports::raw; + use super::{metaloadfn, wayland_storage}; + + #[inline] + #[allow(dead_code)] + pub fn is_loaded() -> bool { + unsafe { wayland_storage::DebugMessageControlKHR.is_loaded } + } + + #[allow(dead_code)] + pub fn load_with(mut loadfn: F) + where + F: FnMut(&'static str) -> *const raw::c_void, + { + unsafe { + wayland_storage::DebugMessageControlKHR = + FnPtr::new(metaloadfn(&mut loadfn, "eglDebugMessageControlKHR", &[])) + } + } } #[allow(non_snake_case)]