#![allow(dead_code)]
#![allow(missing_docs)]
#![allow(non_snake_case)]
#![allow(unknown_lints)]
#![allow(clippy::clippy::unreadable_literal)]
use crate::hresult::Win32Error;
use std::alloc;
use std::ptr;
use std::sync::atomic::{self, AtomicUsize, Ordering};
use winapi::ctypes::c_void;
use winapi::shared::guiddef::{IsEqualIID, REFIID};
use winapi::shared::minwindef::ULONG;
use winapi::shared::ntdef::HRESULT;
use winapi::shared::winerror::{E_INVALIDARG, E_NOINTERFACE};
use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl};
use winapi::Interface;
RIDL! {#[uuid(0x6111e7c4, 0x3ea4, 0x47ed, 0xb0, 0x74, 0xc6, 0x38, 0x87, 0x52, 0x82, 0xc4)]
interface ISoundCore(ISoundCoreVtbl): IUnknown(IUnknownVtbl) {
fn BindHardware(
hardware_info: *const HardwareInfo,
) -> HRESULT,
fn EnumContexts(
index: u32,
context_info: *mut ContextInfo,
) -> HRESULT,
fn GetContextInfo(
context: u32,
context_info: *mut ContextInfo,
) -> HRESULT,
fn GetContext(
context: *mut u32,
) -> HRESULT,
fn SetContext(
context: u32,
restore_state: u32,
) -> HRESULT,
fn EnumFeatures(
context: u32,
index: u32,
feature_info: *mut FeatureInfo,
) -> HRESULT,
fn GetFeatureInfo(
context: u32,
feature: u32,
feature_info: *mut FeatureInfo,
) -> HRESULT,
fn EnumParams(
context: u32,
index: u32,
feature: u32,
param_info: *mut ParamInfo,
) -> HRESULT,
fn GetParamInfo(
param: Param,
info: *mut ParamInfo,
) -> HRESULT,
fn GetParamValue(
param: Param,
value: *mut ParamValue,
) -> HRESULT,
fn SetParamValue(
param: Param,
value: ParamValue,
) -> HRESULT,
fn GetParamValueEx(
param: Param,
paramSize: *mut u32,
paramData: *mut u8,
) -> HRESULT,
fn SetParamValueEx(
param: Param,
paramSize: u32,
paramData: *const u8,
) -> HRESULT,
fn ValidateParamValue(
param: Param,
paramValue: ParamValue,
) -> HRESULT,
fn ValidateParamValueEx(
param: Param,
paramSize: u32,
paramData: *const u8,
) -> HRESULT,
}}
#[repr(C)]
#[derive(Debug)]
pub struct Param {
pub param: u32,
pub feature: u32,
pub context: u32,
}
#[repr(C)]
#[derive(Debug)]
pub struct ParamValue {
pub kind: u32,
pub value: u32,
}
#[repr(C)]
pub struct HardwareInfo {
pub info_type: u32,
pub info: [u16; 260],
}
#[repr(C)]
#[derive(Debug)]
pub struct ContextInfo {
pub context_id: u32,
pub description: [u8; 32],
}
#[repr(C)]
#[derive(Debug)]
pub struct FeatureInfo {
pub feature_id: u32,
pub description: [u8; 32],
pub version: [u8; 16],
}
#[repr(C)]
#[derive(Debug)]
pub struct ParamInfo {
pub param: Param,
pub param_type: u32,
pub data_size: u32,
pub min_value: ParamValue,
pub max_value: ParamValue,
pub step_size: ParamValue,
pub default_value: ParamValue,
pub param_attributes: u32,
pub description: [u8; 32],
}
RIDL! {#[uuid(0xf6cb394a, 0xa680, 0x45c0, 0xac, 0xd2, 0xf0, 0x59, 0x56, 0x26, 0xa3, 0xfd)]
interface IEventNotify(IEventNotifyVtbl): IUnknown(IUnknownVtbl) {
fn RegisterEventCallback(
eventMask: u32,
callback: *mut ICallback,
) -> HRESULT,
fn UnregisterEventCallback() -> HRESULT,
}}
RIDL! {#[uuid(0xb353c442, 0xc49d, 0x4532, 0x9e, 0x3a, 0x1b, 0x20, 0xa1, 0x82, 0xfd, 0x00)]
interface ICallback(ICallbackVtbl): IUnknown(IUnknownVtbl) {
fn EventCallback(
eventInfo: EventInfo,
) -> HRESULT,
}}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct EventInfo {
pub event: u32,
pub data_or_feature_id: u32,
pub param_id: u32,
}
#[repr(C)]
struct Callback<C>
where
C: FnMut(&EventInfo) -> Result<(), Win32Error>,
{
lpVtbl: *mut ICallbackVtbl,
vtbl: ICallbackVtbl,
refs: AtomicUsize,
callback: C,
}
impl ICallback {
#[allow(clippy::new_ret_no_self)]
pub unsafe fn new<C>(callback: C) -> *mut Self
where
C: Send + 'static + FnMut(&EventInfo) -> Result<(), Win32Error>,
{
let mut value = Box::new(Callback::<C> {
lpVtbl: ptr::null_mut(),
vtbl: ICallbackVtbl {
parent: IUnknownVtbl {
QueryInterface: callback_query_interface::<C>,
AddRef: callback_add_ref::<C>,
Release: callback_release::<C>,
},
EventCallback: callback_event_callback::<C>,
},
refs: AtomicUsize::new(1),
callback,
});
value.lpVtbl = &mut value.vtbl as *mut _;
Box::into_raw(value) as *mut Self
}
}
unsafe fn validate<I, C>(this: *mut I) -> Result<*mut Callback<C>, Win32Error>
where
I: Interface,
C: FnMut(&EventInfo) -> Result<(), Win32Error>,
{
let this = this as *mut IUnknown;
if this.is_null()
|| (*this).lpVtbl.is_null()
|| (*(*this).lpVtbl).QueryInterface as usize != callback_query_interface::<C> as usize
{
Err(Win32Error::new(E_INVALIDARG))
} else {
Ok(this as *mut Callback<C>)
}
}
unsafe fn uncheck<E>(result: E) -> HRESULT
where
E: FnOnce() -> Result<HRESULT, Win32Error>,
{
match result() {
Ok(result) => result,
Err(Win32Error { code, .. }) => code,
}
}
unsafe extern "system" fn callback_query_interface<C>(
this: *mut IUnknown,
iid: REFIID,
object: *mut *mut c_void,
) -> HRESULT
where
C: FnMut(&EventInfo) -> Result<(), Win32Error>,
{
uncheck(|| {
let this = validate::<_, C>(this)?;
let iid = iid.as_ref().unwrap();
if IsEqualIID(iid, &IUnknown::uuidof()) || IsEqualIID(iid, &ICallback::uuidof()) {
(*this).refs.fetch_add(1, Ordering::Relaxed);
*object = this as *mut c_void;
Ok(0)
} else {
*object = ptr::null_mut();
Err(Win32Error::new(E_NOINTERFACE))
}
})
}
unsafe extern "system" fn callback_add_ref<C>(this: *mut IUnknown) -> ULONG
where
C: FnMut(&EventInfo) -> Result<(), Win32Error>,
{
match validate::<_, C>(this) {
Ok(this) => {
let count = (*this).refs.fetch_add(1, Ordering::Relaxed) + 1;
count as ULONG
}
Err(_) => 1,
}
}
unsafe extern "system" fn callback_release<C>(this: *mut IUnknown) -> ULONG
where
C: FnMut(&EventInfo) -> Result<(), Win32Error>,
{
match validate::<_, C>(this) {
Ok(this) => {
let count = (*this).refs.fetch_sub(1, Ordering::Release) - 1;
if count == 0 {
atomic::fence(Ordering::Acquire);
ptr::drop_in_place(this);
alloc::dealloc(
this as *mut u8,
alloc::Layout::for_value(this.as_ref().unwrap()),
);
}
count as ULONG
}
Err(_) => 1,
}
}
unsafe extern "system" fn callback_event_callback<C>(
this: *mut ICallback,
event_info: EventInfo,
) -> HRESULT
where
C: FnMut(&EventInfo) -> Result<(), Win32Error>,
{
uncheck(|| {
let this = validate::<_, C>(this)?;
((*this).callback)(&event_info)?;
Ok(0)
})
}