use crate::{
Context, ExternalTexture, ExternalTextureMetadata, LayoutPoint, LayoutRect, LayoutSize,
MichiuSoA,
};
use std::sync::Arc;
use windows::Win32::Foundation::{HWND, LPARAM, POINT, WPARAM};
use windows::Win32::Graphics::DirectComposition::IDCompositionVisual2;
use windows::Win32::Graphics::Imaging::IWICImagingFactory;
#[derive(Debug, Clone)]
pub struct StaticExternalTexture {
pub view: wgpu::TextureView,
pub metadata: ExternalTextureMetadata,
}
impl ExternalTexture for StaticExternalTexture {
fn resolve_view(&self, _device: &wgpu::Device, _queue: &wgpu::Queue) -> wgpu::TextureView {
self.view.clone()
}
fn metadata(&self) -> ExternalTextureMetadata {
self.metadata
}
}
pub trait ExternalVisual: Send + Sync {
fn resolve_visual(&self) -> IDCompositionVisual2;
fn static_texture(&self) -> Option<Arc<dyn ExternalTexture>> {
None
}
fn update(&self, _cx: &VisualUpdateContext) {}
fn handle_raw_input(
&self,
_msg: u32,
_wparam: WPARAM,
_lparam: LPARAM,
_local_phys_pos: POINT,
) -> bool {
false
}
fn metadata(&self) -> ExternalVisualMetadata;
}
impl<T: ExternalVisual + ?Sized> ExternalVisual for Arc<T> {
fn resolve_visual(&self) -> IDCompositionVisual2 {
(**self).resolve_visual()
}
fn static_texture(&self) -> Option<Arc<dyn ExternalTexture>> {
(**self).static_texture()
}
fn update(&self, cx: &VisualUpdateContext) {
(**self).update(cx);
}
fn handle_raw_input(
&self,
msg: u32,
wparam: WPARAM,
lparam: LPARAM,
local_phys_pos: POINT,
) -> bool {
(**self).handle_raw_input(msg, wparam, lparam, local_phys_pos)
}
fn metadata(&self) -> ExternalVisualMetadata {
(**self).metadata()
}
}
pub struct VisualUpdateContext<'a> {
pub hwnd: HWND,
pub rect: LayoutRect,
pub scale_factor: f32,
pub is_interactive: bool,
pub is_stable: bool,
pub device: &'a wgpu::Device,
pub queue: &'a wgpu::Queue,
pub wic_factory: &'a IWICImagingFactory,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ExternalVisualMetadata {
pub size: LayoutSize,
pub auto_clip: bool,
pub auto_transform: bool,
}
impl Default for ExternalVisualMetadata {
fn default() -> Self {
Self {
size: LayoutSize::ZERO,
auto_clip: true,
auto_transform: true,
}
}
}
pub(crate) fn dispatch_raw_input_to_external_visual(
cx: &mut Context,
msg: u32,
wparam: WPARAM,
lparam: LPARAM,
window_phys_pos: LayoutPoint,
scale_factor: f32,
) -> bool {
let logical_pos = LayoutPoint::new(
window_phys_pos.x / scale_factor,
window_phys_pos.y / scale_factor,
);
let target_id = cx
.events
.evt_interaction_states
.pressed
.or_else(|| cx.hit_test(logical_pos));
let Some(id) = target_id else {
return false;
};
if !cx
.topology
.topo_active_masks
.at(id)
.has_external_visual_content()
{
return false;
}
let Some(visual) = cx.contents.cont_external_visual.find(id).cloned() else {
return false;
};
let rect = cx.outputs.out_rects.find_or_default(id, &mut cx.debug);
#[expect(clippy::cast_possible_truncation)]
let local_phys_pos = POINT {
x: (window_phys_pos.x - rect.x * scale_factor).round() as i32,
y: (window_phys_pos.y - rect.y * scale_factor).round() as i32,
};
visual.handle_raw_input(msg, wparam, lparam, local_phys_pos)
}