use std::ffi::c_void; use std::io::{stderr, Write}; use std::mem::transmute; use std::panic::catch_unwind; use util_macros::FromRepr; use crate::CurrentContextWithFns; pub fn set_debug_message_callback( current_context: &CurrentContextWithFns<'_>, cb: MessageCallback, ) { unsafe { current_context .fns() .DebugMessageCallback(Some(debug_message_cb), cb as *mut c_void); } } /// Sets debug message parameters. /// /// # Errors /// Returns `Err` if `ids` contains too many ids. pub fn set_debug_message_control( current_context: &CurrentContextWithFns<'_>, source: Option, ty: Option, severity: Option, ids: &[u32], ids_action: MessageIdsAction, ) -> Result<(), SetDebugMessageControlError> { let ids_len: crate::sys::types::GLsizei = ids.len() .try_into() .map_err(|_| SetDebugMessageControlError::TooManyIds { id_cnt: ids.len(), max_id_cnt: crate::sys::types::GLsizei::MAX as usize, })?; unsafe { current_context.fns().DebugMessageControl( source.map_or(crate::sys::DONT_CARE, |source| source as u32), ty.map_or(crate::sys::DONT_CARE, |ty| ty as u32), severity.map_or(crate::sys::DONT_CARE, |severity| severity as u32), ids_len, ids.as_ptr(), ids_action as u8, ); } Ok(()) } #[derive(Debug, thiserror::Error)] pub enum SetDebugMessageControlError { #[error("Too many ids provided ({id_cnt}). Must be < {max_id_cnt}")] TooManyIds { id_cnt: usize, max_id_cnt: usize }, } pub type MessageCallback = fn( source: MessageSource, ty: MessageType, id: u32, severity: MessageSeverity, message: &str, ); #[derive(Debug, Clone, Copy)] #[repr(u8)] // GLboolean = u8 pub enum MessageIdsAction { Enable = crate::sys::TRUE, Disable = crate::sys::FALSE, } #[derive(Debug, Clone, Copy, FromRepr)] #[repr(u32)] // GLenum = u32 pub enum MessageSource { Api = crate::sys::DEBUG_SOURCE_API, WindowSystem = crate::sys::DEBUG_SOURCE_WINDOW_SYSTEM, ShaderCompiler = crate::sys::DEBUG_SOURCE_SHADER_COMPILER, ThirdParty = crate::sys::DEBUG_SOURCE_THIRD_PARTY, Application = crate::sys::DEBUG_SOURCE_APPLICATION, Other = crate::sys::DEBUG_SOURCE_OTHER, } #[derive(Debug, Clone, Copy, FromRepr)] #[repr(u32)] // GLenum = u32 pub enum MessageType { DeprecatedBehavior = crate::sys::DEBUG_TYPE_DEPRECATED_BEHAVIOR, Error = crate::sys::DEBUG_TYPE_ERROR, Marker = crate::sys::DEBUG_TYPE_MARKER, Other = crate::sys::DEBUG_TYPE_OTHER, Performance = crate::sys::DEBUG_TYPE_PERFORMANCE, PopGroup = crate::sys::DEBUG_TYPE_POP_GROUP, PushGroup = crate::sys::DEBUG_TYPE_PUSH_GROUP, Portability = crate::sys::DEBUG_TYPE_PORTABILITY, UndefinedBehavior = crate::sys::DEBUG_TYPE_UNDEFINED_BEHAVIOR, } #[derive(Debug, Clone, Copy, FromRepr)] #[repr(u32)] // GLenum = u32 pub enum MessageSeverity { High = crate::sys::DEBUG_SEVERITY_HIGH, Medium = crate::sys::DEBUG_SEVERITY_MEDIUM, Low = crate::sys::DEBUG_SEVERITY_LOW, Notification = crate::sys::DEBUG_SEVERITY_NOTIFICATION, } extern "system" fn debug_message_cb( source: crate::sys::types::GLenum, ty: crate::sys::types::GLenum, id: crate::sys::types::GLuint, severity: crate::sys::types::GLenum, message_length: crate::sys::types::GLsizei, message: *const crate::sys::types::GLchar, user_cb: *mut c_void, ) { let user_cb = unsafe { transmute::<*mut c_void, MessageCallback>(user_cb) }; let Ok(msg_length) = usize::try_from(message_length) else { return; }; // Unwinds are catched because unwinding from Rust code into foreign code is UB. let res = catch_unwind(|| { let msg_source = MessageSource::from_repr(source).unwrap(); let msg_type = MessageType::from_repr(ty).unwrap(); let msg_severity = MessageSeverity::from_repr(severity).unwrap(); // SAFETY: The received message should be a valid ASCII string let message = unsafe { std::str::from_utf8_unchecked(std::slice::from_raw_parts( message.cast(), msg_length, )) }; user_cb(msg_source, msg_type, id, msg_severity, message); }); if res.is_err() { // eprintln is not used since it can panic and unwinds are unwanted because // unwinding from Rust code into foreign code is UB. stderr() .write_all(b"ERROR: Panic in debug message callback") .ok(); println!(); } }