use crate::*;
use std::{cell::Cell, rc::Rc};
use windows::Win32::Foundation::{E_INVALIDARG, E_NOTIMPL, E_POINTER};
use windows_core::{PCSTR, PCWSTR, implement};
use windy::{WStr, traits::ToWString};
enum_flags! {
pub enum DebugOutCbKind: u32 {
Text = DEBUG_OUTCB_TEXT,
Dml = DEBUG_OUTCB_DML,
ExplicitFlush = DEBUG_OUTCB_EXPLICIT_FLUSH,
}
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct DebugOutCbiFlags: u32 {
const ExplicitFlush = DEBUG_OUTCBI_EXPLICIT_FLUSH;
const Text = DEBUG_OUTCBI_TEXT;
const Dml = DEBUG_OUTCBI_DML;
const AnyFormat = DEBUG_OUTCBI_ANY_FORMAT;
}
}
bitflags::bitflags! {
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct DebugOutCbfFlags: u32 {
const CombinedExplicitFlush = DEBUG_OUTCBF_COMBINED_EXPLICIT_FLUSH;
const DmlHasTags = DEBUG_OUTCBF_DML_HAS_TAGS;
const DmlHasSpecialCharacters = DEBUG_OUTCBF_DML_HAS_SPECIAL_CHARACTERS;
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum DebugOutCbText {
ExplicitFlush,
Text(String),
Dml(String),
}
impl DebugOutCbText {
pub(crate) fn from_arg(
which: u32,
text: &PCWSTR,
) -> windows::core::Result<Self> {
let kind = DebugOutCbKind::try_from(which)
.map_err(|_| windows::core::Error::from(E_INVALIDARG))?;
Ok(match kind {
DebugOutCbKind::ExplicitFlush => Self::ExplicitFlush,
DebugOutCbKind::Text => {
if text.is_null() {
return Err(E_POINTER.into());
}
Self::Text(unsafe { WStr::from_raw(text.0).to_string() })
}
DebugOutCbKind::Dml => {
if text.is_null() {
return Err(E_POINTER.into());
}
Self::Dml(unsafe { WStr::from_raw(text.0).to_string() })
}
})
}
pub fn kind(&self) -> DebugOutCbKind {
match self {
Self::ExplicitFlush => DebugOutCbKind::ExplicitFlush,
Self::Text(_) => DebugOutCbKind::Text,
Self::Dml(_) => DebugOutCbKind::Dml,
}
}
pub fn get_text(&self) -> Option<&str> {
match self {
DebugOutCbText::ExplicitFlush => None,
DebugOutCbText::Text(s) | DebugOutCbText::Dml(s) => Some(s),
}
}
}
#[allow(unused_variables)]
pub trait DebugOutputCallbacksHandler {
fn get_interest_mask(&self) -> windows::core::Result<DebugOutCbiFlags> {
Ok(DebugOutCbiFlags::all())
}
fn output2(
&self,
flags: DebugOutCbfFlags,
arg: DebugOutputFlags,
text: &DebugOutCbText,
) -> windows::core::Result<()> {
Ok(())
}
}
impl_debug_interface!(
DebugOutputCallbacks,
DebugOutputCallbacksRef,
IDebugOutputCallbacks2,
IDebugOutputCallbacks
);
impl From<DebugOutputCallbacks> for IDebugOutputCallbacks {
fn from(value: DebugOutputCallbacks) -> Self { value.0.cast().unwrap() }
}
impl From<DebugOutputCallbacks> for IDebugOutputCallbacks2 {
fn from(value: DebugOutputCallbacks) -> Self { value.0 }
}
impl DebugOutputCallbacksHandler for DebugOutputCallbacks {
fn get_interest_mask(&self) -> windows::core::Result<DebugOutCbiFlags> {
unsafe {
Ok(DebugOutCbiFlags::from_bits_retain(
self.0.GetInterestMask()?,
))
}
}
fn output2(
&self,
flags: DebugOutCbfFlags,
arg: DebugOutputFlags,
text: &DebugOutCbText,
) -> windows::core::Result<()> {
let which = text.kind();
let text = text.get_text().map(|x| x.to_wstring());
let text_ptr = text.as_ref().map_or(std::ptr::null(), |x| x.as_ptr());
unsafe {
self.0.Output2(
which as u32,
flags.bits(),
arg.bits() as u64,
PCWSTR(text_ptr),
)
}
}
}
#[implement(IDebugOutputCallbacks, IDebugOutputCallbacks2)]
pub struct DebugOutputCallbacksAdapter {
pub callbacks: Rc<dyn DebugOutputCallbacksHandler>,
poisoned: Cell<bool>,
}
impl DebugOutputCallbacksAdapter {
pub fn new(callbacks: Rc<dyn DebugOutputCallbacksHandler>) -> Self {
Self {
callbacks,
poisoned: Cell::new(false),
}
}
pub fn as_handler(&self) -> &dyn DebugOutputCallbacksHandler {
self.callbacks.as_ref()
}
pub fn into_callbacks(self) -> DebugOutputCallbacks {
let interface: IDebugOutputCallbacks2 = self.into();
interface.into()
}
impl_callbacks_catch_unwind!();
}
#[allow(non_snake_case)]
impl IDebugOutputCallbacks_Impl for DebugOutputCallbacksAdapter_Impl {
fn Output(&self, _mask: u32, _text: &PCSTR) -> windows::core::Result<()> {
Err(E_NOTIMPL.into())
}
}
#[allow(non_snake_case)]
impl IDebugOutputCallbacks2_Impl for DebugOutputCallbacksAdapter_Impl {
fn Output(&self, _mask: u32, _text: &PCSTR) -> windows::core::Result<()> {
Err(E_NOTIMPL.into())
}
fn GetInterestMask(&self) -> windows::core::Result<u32> {
self.__catch_unwind(|| Ok(self.callbacks.get_interest_mask()?.bits()))
}
fn Output2(
&self,
which: u32,
flags: u32,
arg: u64,
text: &PCWSTR,
) -> windows::core::Result<()> {
self.__catch_unwind(|| {
let text = DebugOutCbText::from_arg(which, text)?;
let arg = u32::try_from(arg)
.map_err(|_| windows::core::Error::from(E_INVALIDARG))?;
self.callbacks.output2(
DebugOutCbfFlags::from_bits_retain(flags),
DebugOutputFlags::from_bits_retain(arg),
&text,
)
})
}
}
impl From<Rc<dyn DebugOutputCallbacksHandler>> for DebugOutputCallbacksAdapter {
fn from(value: Rc<dyn DebugOutputCallbacksHandler>) -> Self {
Self {
callbacks: value,
poisoned: Cell::new(false),
}
}
}
#[allow(unused_variables)]
pub trait DebugOutputCallbacksWideHandler {
fn output(
&self,
mask: DebugOutputFlags,
text: String,
) -> windows::core::Result<()> {
Ok(())
}
}
impl_debug_interface!(
DebugOutputCallbacksWide,
DebugOutputCallbacksWideRef,
IDebugOutputCallbacksWide
);
impl DebugOutputCallbacksWideHandler for DebugOutputCallbacksWide {
fn output(
&self,
mask: DebugOutputFlags,
text: String,
) -> windows::core::Result<()> {
let text = text.to_wstring();
unsafe { self.0.Output(mask.bits(), PCWSTR(text.as_ptr())) }
}
}
#[implement(IDebugOutputCallbacksWide)]
pub struct DebugOutputCallbacksWideAdapter {
pub callbacks: Rc<dyn DebugOutputCallbacksWideHandler>,
poisoned: Cell<bool>,
}
impl DebugOutputCallbacksWideAdapter {
pub fn new(callbacks: Rc<dyn DebugOutputCallbacksWideHandler>) -> Self {
Self {
callbacks,
poisoned: Cell::new(false),
}
}
pub fn as_handler(&self) -> &dyn DebugOutputCallbacksWideHandler {
self.callbacks.as_ref()
}
pub fn into_callbacks(self) -> DebugOutputCallbacksWide {
self.into_interface().into()
}
pub fn into_interface(self) -> IDebugOutputCallbacksWide { self.into() }
impl_callbacks_catch_unwind!();
}
#[allow(non_snake_case)]
impl IDebugOutputCallbacksWide_Impl for DebugOutputCallbacksWideAdapter_Impl {
fn Output(&self, mask: u32, text: &PCWSTR) -> windows::core::Result<()> {
self.__catch_unwind(|| {
if text.is_null() {
return Err(E_POINTER.into());
}
self.callbacks
.output(DebugOutputFlags::from_bits_retain(mask), unsafe {
WStr::from_raw(text.0).to_string()
})
})
}
}
impl<T: DebugOutputCallbacksWideHandler + 'static> From<Rc<T>>
for DebugOutputCallbacksWideAdapter
{
fn from(value: Rc<T>) -> Self {
Self {
callbacks: value,
poisoned: Cell::new(false),
}
}
}