use crate::{get_string, AllenResult, Buffer, Device, Listener, Source};
use lazy_static::lazy_static;
use std::{
ffi::CString,
ptr,
sync::{Arc, Mutex, MutexGuard},
};
use oal_sys_windows::*;
lazy_static! {
static ref SINGLE_CONTEXT_LOCK: Mutex<()> = Mutex::new(());
}
pub(crate) struct ContextInner {
handle: *mut ALCcontext,
device: Device,
}
impl Drop for ContextInner {
fn drop(&mut self) {
unsafe { alcDestroyContext(self.handle) };
}
}
#[derive(Clone)]
pub struct Context {
inner: Arc<ContextInner>,
}
impl Context {
pub(crate) fn new(device: Device) -> AllenResult<Context> {
let handle = unsafe { alcCreateContext(device.inner.handle, ptr::null()) };
if handle == ptr::null_mut() {
Err(device.check_alc_error().expect_err("handle is null"))
} else {
unsafe {
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
}
Ok(Self {
inner: Arc::new(ContextInner { handle, device }),
})
}
}
pub fn make_current(&self) -> Option<MutexGuard<()>> {
let function: PFNALCSETTHREADCONTEXTPROC = unsafe {
let name = CString::new("alcSetThreadContext").unwrap();
std::mem::transmute(alcGetProcAddress(
ptr::null_mut(),
name.as_ptr() as *const ALCchar,
))
};
if let Some(function) = function {
unsafe {
function(self.inner.handle);
}
None
} else {
assert_eq!(true as i8, unsafe {
alcMakeContextCurrent(self.inner.handle)
});
Some(SINGLE_CONTEXT_LOCK.lock().unwrap())
}
}
pub fn is_current(&self) -> bool {
let current_context = {
let function: PFNALCGETTHREADCONTEXTPROC = unsafe {
let name = CString::new("alcGetThreadContext").unwrap();
std::mem::transmute(alcGetProcAddress(
ptr::null_mut(),
name.as_ptr() as *const ALCchar,
))
};
if let Some(function) = function {
unsafe { function() }
} else {
unsafe { alcGetCurrentContext() }
}
};
current_context == self.inner.handle
}
pub fn vendor(&self) -> &'static str {
let _lock = self.make_current();
get_string(AL_VENDOR)
}
pub fn version(&self) -> &'static str {
let _lock = self.make_current();
get_string(AL_VERSION)
}
pub fn renderer(&self) -> &'static str {
let _lock = self.make_current();
get_string(AL_RENDERER)
}
pub fn extensions(&self) -> &'static str {
let _lock = self.make_current();
get_string(AL_EXTENSIONS)
}
pub fn listener(&self) -> Listener {
Listener::new(self.clone())
}
pub fn new_buffer(&self) -> AllenResult<Buffer> {
Buffer::new(self.clone())
}
pub fn new_source(&self) -> AllenResult<Source> {
Source::new(self.clone())
}
pub fn suspend(&self) -> AllenResult<()> {
let _lock = self.make_current();
unsafe {
alcSuspendContext(self.inner.handle);
}
self.inner.device.check_alc_error()?;
Ok(())
}
pub fn process(&self) -> AllenResult<()> {
let _lock = self.make_current();
unsafe {
alcProcessContext(self.inner.handle);
}
self.inner.device.check_alc_error()?;
Ok(())
}
}