diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index dbc7ff508f..d1e46bfe62 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -151,6 +151,10 @@ jobs: run: cargo clippy -p test_component - name: Clippy test_component_client run: cargo clippy -p test_component_client + - name: Clippy test_composable + run: cargo clippy -p test_composable + - name: Clippy test_composable_client + run: cargo clippy -p test_composable_client - name: Clippy test_const_fields run: cargo clippy -p test_const_fields - name: Clippy test_const_params diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6fcb232280..a078f368e1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -177,6 +177,10 @@ jobs: run: cargo test -p test_component --target ${{ matrix.target }} ${{ matrix.etc }} - name: Test test_component_client run: cargo test -p test_component_client --target ${{ matrix.target }} ${{ matrix.etc }} + - name: Test test_composable + run: cargo test -p test_composable --target ${{ matrix.target }} ${{ matrix.etc }} + - name: Test test_composable_client + run: cargo test -p test_composable_client --target ${{ matrix.target }} ${{ matrix.etc }} - name: Test test_const_fields run: cargo test -p test_const_fields --target ${{ matrix.target }} ${{ matrix.etc }} - name: Test test_const_params @@ -251,12 +255,12 @@ jobs: run: cargo test -p test_reference_float --target ${{ matrix.target }} ${{ matrix.etc }} - name: Test test_registry run: cargo test -p test_registry --target ${{ matrix.target }} ${{ matrix.etc }} + - name: Clean + run: cargo clean - name: Test test_registry_default run: cargo test -p test_registry_default --target ${{ matrix.target }} ${{ matrix.etc }} - name: Test test_reserved run: cargo test -p test_reserved --target ${{ matrix.target }} ${{ matrix.etc }} - - name: Clean - run: cargo clean - name: Test test_resources run: cargo test -p test_resources --target ${{ matrix.target }} ${{ matrix.etc }} - name: Test test_result diff --git a/crates/tests/winrt/composable/Cargo.toml b/crates/tests/winrt/composable/Cargo.toml new file mode 100644 index 0000000000..723baeed14 --- /dev/null +++ b/crates/tests/winrt/composable/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "test_composable" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] +doc = false +doctest = false + +[build-dependencies.windows-bindgen] +workspace = true + +[dependencies.windows-core] +workspace = true + +[dependencies.windows] +workspace = true +features = [ + "implement", + "Foundation", + "Win32_System_WinRT", +] diff --git a/crates/tests/winrt/composable/build.rs b/crates/tests/winrt/composable/build.rs new file mode 100644 index 0000000000..bca0687eaa --- /dev/null +++ b/crates/tests/winrt/composable/build.rs @@ -0,0 +1,33 @@ +fn main() { + let mut command = std::process::Command::new("midlrt.exe"); + command.args([ + "/winrt", + "/nomidl", + "/h", + "nul", + "/metadata_dir", + "../../../libs/bindgen/default", + "/reference", + "../../../libs/bindgen/default/Windows.winmd", + "/winmd", + "metadata.winmd", + "src/metadata.idl", + ]); + + if !command.status().unwrap().success() { + panic!("Failed to run midlrt"); + } + + windows_bindgen::bindgen([ + "--in", + "metadata.winmd", + "--out", + "src/bindings.rs", + "--filter", + "test_composable", + "--config", + "implement", + "no-bindgen-comment", + ]) + .unwrap(); +} diff --git a/crates/tests/winrt/composable/src/bindings.rs b/crates/tests/winrt/composable/src/bindings.rs new file mode 100644 index 0000000000..afc2fae057 --- /dev/null +++ b/crates/tests/winrt/composable/src/bindings.rs @@ -0,0 +1,474 @@ +#![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + dead_code, + clippy::all +)] +windows_core::imp::define_interface!( + ICompositor, + ICompositor_Vtbl, + 0xac7b49b8_e092_52ad_8456_48696a5a258e +); +impl windows_core::RuntimeType for ICompositor { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct ICompositor_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub CreateSpriteVisual: unsafe extern "system" fn( + *mut core::ffi::c_void, + i32, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub CreateContainerVisual: unsafe extern "system" fn( + *mut core::ffi::c_void, + i32, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, +} +windows_core::imp::define_interface!( + IContainerVisual, + IContainerVisual_Vtbl, + 0xb8accc46_3ff7_5a24_8247_f5a52e1f5a8d +); +impl windows_core::RuntimeType for IContainerVisual { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IContainerVisual_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Children: + unsafe extern "system" fn(*mut core::ffi::c_void, *mut i32) -> windows_core::HRESULT, +} +windows_core::imp::define_interface!( + IContainerVisualFactory, + IContainerVisualFactory_Vtbl, + 0x558b6180_1a65_5f01_8be2_2cc0b2034c0e +); +impl windows_core::RuntimeType for IContainerVisualFactory { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IContainerVisualFactory_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, +} +windows_core::imp::define_interface!( + ISpriteVisual, + ISpriteVisual_Vtbl, + 0x25f23ebe_4cd3_5349_b16d_d88c4d852ea1 +); +impl windows_core::RuntimeType for ISpriteVisual { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct ISpriteVisual_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Brush: unsafe extern "system" fn(*mut core::ffi::c_void, *mut i32) -> windows_core::HRESULT, +} +windows_core::imp::define_interface!( + IVisual, + IVisual_Vtbl, + 0xce89606a_5b03_5861_af26_9dced3aab7e6 +); +impl windows_core::RuntimeType for IVisual { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IVisual_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Compositor: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, +} +windows_core::imp::define_interface!( + IVisualFactory, + IVisualFactory_Vtbl, + 0x1974545d_259f_553c_8ea0_e505f897df81 +); +impl windows_core::RuntimeType for IVisualFactory { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IVisualFactory_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, +} +#[repr(transparent)] +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Compositor(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + Compositor, + windows_core::IUnknown, + windows_core::IInspectable +); +impl Compositor { + pub fn new() -> windows_core::Result { + Self::IActivationFactory(|f| f.ActivateInstance::()) + } + fn IActivationFactory< + R, + F: FnOnce(&windows_core::imp::IGenericFactory) -> windows_core::Result, + >( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache< + Compositor, + windows_core::imp::IGenericFactory, + > = windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } + pub fn CreateSpriteVisual(&self, brush: i32) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateSpriteVisual)( + windows_core::Interface::as_raw(this), + brush, + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn CreateContainerVisual(&self, children: i32) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateContainerVisual)( + windows_core::Interface::as_raw(this), + children, + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl windows_core::RuntimeType for Compositor { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for Compositor { + type Vtable = ICompositor_Vtbl; + const IID: windows_core::GUID = ::IID; +} +impl windows_core::RuntimeName for Compositor { + const NAME: &'static str = "test_composable.Compositor"; +} +unsafe impl Send for Compositor {} +unsafe impl Sync for Compositor {} +#[repr(transparent)] +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct ContainerVisual(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + ContainerVisual, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!(ContainerVisual, Visual); +impl ContainerVisual { + pub fn Children(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Children)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Compositor(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Compositor)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl windows_core::RuntimeType for ContainerVisual { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for ContainerVisual { + type Vtable = IContainerVisual_Vtbl; + const IID: windows_core::GUID = ::IID; +} +impl windows_core::RuntimeName for ContainerVisual { + const NAME: &'static str = "test_composable.ContainerVisual"; +} +unsafe impl Send for ContainerVisual {} +unsafe impl Sync for ContainerVisual {} +#[repr(transparent)] +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct SpriteVisual(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + SpriteVisual, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!(SpriteVisual, ContainerVisual, Visual); +impl SpriteVisual { + pub fn Children(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Children)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Brush(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Brush)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Compositor(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Compositor)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl windows_core::RuntimeType for SpriteVisual { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for SpriteVisual { + type Vtable = ISpriteVisual_Vtbl; + const IID: windows_core::GUID = ::IID; +} +impl windows_core::RuntimeName for SpriteVisual { + const NAME: &'static str = "test_composable.SpriteVisual"; +} +unsafe impl Send for SpriteVisual {} +unsafe impl Sync for SpriteVisual {} +#[repr(transparent)] +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Visual(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!(Visual, windows_core::IUnknown, windows_core::IInspectable); +impl Visual { + pub fn Compositor(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Compositor)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl windows_core::RuntimeType for Visual { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for Visual { + type Vtable = IVisual_Vtbl; + const IID: windows_core::GUID = ::IID; +} +impl windows_core::RuntimeName for Visual { + const NAME: &'static str = "test_composable.Visual"; +} +unsafe impl Send for Visual {} +unsafe impl Sync for Visual {} +pub trait ICompositor_Impl: Sized + windows_core::IUnknownImpl { + fn CreateSpriteVisual(&self, brush: i32) -> windows_core::Result; + fn CreateContainerVisual(&self, children: i32) -> windows_core::Result; +} +impl windows_core::RuntimeName for ICompositor { + const NAME: &'static str = "test_composable.ICompositor"; +} +impl ICompositor_Vtbl { + pub const fn new() -> ICompositor_Vtbl { + unsafe extern "system" fn CreateSpriteVisual< + Identity: ICompositor_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + brush: i32, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match ICompositor_Impl::CreateSpriteVisual(this, brush) { + Ok(ok__) => { + result__.write(core::mem::transmute_copy(&ok__)); + core::mem::forget(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + unsafe extern "system" fn CreateContainerVisual< + Identity: ICompositor_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + children: i32, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match ICompositor_Impl::CreateContainerVisual(this, children) { + Ok(ok__) => { + result__.write(core::mem::transmute_copy(&ok__)); + core::mem::forget(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::(), + CreateSpriteVisual: CreateSpriteVisual::, + CreateContainerVisual: CreateContainerVisual::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +pub trait IContainerVisual_Impl: Sized + windows_core::IUnknownImpl { + fn Children(&self) -> windows_core::Result; +} +impl windows_core::RuntimeName for IContainerVisual { + const NAME: &'static str = "test_composable.IContainerVisual"; +} +impl IContainerVisual_Vtbl { + pub const fn new() -> IContainerVisual_Vtbl + { + unsafe extern "system" fn Children( + this: *mut core::ffi::c_void, + result__: *mut i32, + ) -> windows_core::HRESULT { + let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IContainerVisual_Impl::Children(this) { + Ok(ok__) => { + result__.write(core::mem::transmute_copy(&ok__)); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::(), + Children: Children::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +pub trait IContainerVisualFactory_Impl: Sized + windows_core::IUnknownImpl {} +impl windows_core::RuntimeName for IContainerVisualFactory { + const NAME: &'static str = "test_composable.IContainerVisualFactory"; +} +impl IContainerVisualFactory_Vtbl { + pub const fn new( + ) -> IContainerVisualFactory_Vtbl { + Self { + base__: windows_core::IInspectable_Vtbl::new::( + ), + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +pub trait ISpriteVisual_Impl: Sized + windows_core::IUnknownImpl { + fn Brush(&self) -> windows_core::Result; +} +impl windows_core::RuntimeName for ISpriteVisual { + const NAME: &'static str = "test_composable.ISpriteVisual"; +} +impl ISpriteVisual_Vtbl { + pub const fn new() -> ISpriteVisual_Vtbl { + unsafe extern "system" fn Brush( + this: *mut core::ffi::c_void, + result__: *mut i32, + ) -> windows_core::HRESULT { + let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match ISpriteVisual_Impl::Brush(this) { + Ok(ok__) => { + result__.write(core::mem::transmute_copy(&ok__)); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::(), + Brush: Brush::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +pub trait IVisual_Impl: Sized + windows_core::IUnknownImpl { + fn Compositor(&self) -> windows_core::Result; +} +impl windows_core::RuntimeName for IVisual { + const NAME: &'static str = "test_composable.IVisual"; +} +impl IVisual_Vtbl { + pub const fn new() -> IVisual_Vtbl { + unsafe extern "system" fn Compositor( + this: *mut core::ffi::c_void, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + let this: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IVisual_Impl::Compositor(this) { + Ok(ok__) => { + result__.write(core::mem::transmute_copy(&ok__)); + core::mem::forget(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::(), + Compositor: Compositor::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +pub trait IVisualFactory_Impl: Sized + windows_core::IUnknownImpl {} +impl windows_core::RuntimeName for IVisualFactory { + const NAME: &'static str = "test_composable.IVisualFactory"; +} +impl IVisualFactory_Vtbl { + pub const fn new() -> IVisualFactory_Vtbl { + Self { + base__: windows_core::IInspectable_Vtbl::new::(), + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} diff --git a/crates/tests/winrt/composable/src/lib.rs b/crates/tests/winrt/composable/src/lib.rs new file mode 100644 index 0000000000..e824d91b63 --- /dev/null +++ b/crates/tests/winrt/composable/src/lib.rs @@ -0,0 +1,93 @@ +mod bindings; +use windows::{core::*, Win32::Foundation::*, Win32::System::WinRT::*}; + +#[no_mangle] +unsafe extern "system" fn DllGetActivationFactory( + name: Ref, + factory: OutRef, +) -> HRESULT { + if *name == "test_composable.Compositor" { + factory.write(Some(CompositorFactory.into())).into() + } else { + _ = factory.write(None); + CLASS_E_CLASSNOTAVAILABLE + } +} + +#[implement(IActivationFactory)] +struct CompositorFactory; + +impl IActivationFactory_Impl for CompositorFactory_Impl { + fn ActivateInstance(&self) -> Result { + Ok(Compositor.into()) + } +} + +#[implement(bindings::Compositor)] +struct Compositor; + +impl bindings::ICompositor_Impl for Compositor_Impl { + fn CreateSpriteVisual(&self, brush: i32) -> Result { + Ok(SpriteVisual::new(ContainerVisual::new(self.to_object(), brush * 2), brush).into()) + } + fn CreateContainerVisual(&self, children: i32) -> Result { + Ok(ContainerVisual::new(self.to_object(), children).into()) + } +} + +#[implement(bindings::ContainerVisual, bindings::Visual)] +struct ContainerVisual { + compositor: ComObject, + children: i32, +} + +impl ContainerVisual { + fn new(compositor: ComObject, children: i32) -> Self { + Self { + compositor, + children, + } + } +} + +impl bindings::IContainerVisual_Impl for ContainerVisual_Impl { + fn Children(&self) -> Result { + Ok(self.children) + } +} + +impl bindings::IVisual_Impl for ContainerVisual_Impl { + fn Compositor(&self) -> Result { + Ok(self.compositor.to_interface()) + } +} + +#[implement(bindings::SpriteVisual, bindings::ContainerVisual, bindings::Visual)] +struct SpriteVisual { + container: ContainerVisual, + brush: i32, +} + +impl SpriteVisual { + fn new(container: ContainerVisual, brush: i32) -> Self { + Self { container, brush } + } +} + +impl bindings::ISpriteVisual_Impl for SpriteVisual_Impl { + fn Brush(&self) -> Result { + Ok(self.brush) + } +} + +impl bindings::IContainerVisual_Impl for SpriteVisual_Impl { + fn Children(&self) -> Result { + Ok(self.container.children) + } +} + +impl bindings::IVisual_Impl for SpriteVisual_Impl { + fn Compositor(&self) -> Result { + Ok(self.container.compositor.to_interface()) + } +} diff --git a/crates/tests/winrt/composable/src/metadata.idl b/crates/tests/winrt/composable/src/metadata.idl new file mode 100644 index 0000000000..82288521c5 --- /dev/null +++ b/crates/tests/winrt/composable/src/metadata.idl @@ -0,0 +1,30 @@ +// This tests that `windows-bindgen` can generate bindings and `windows-rs` in general can support implementing and consuming +// closed composable type systems. A closed type system, at least in this simpler MIDL dialect, is distinguished by the lack +// of any constructors on "unsealed" classes and instead depends on static methods for internal construction thus preventing +// binary (open) composition. + +namespace test_composable +{ + runtimeclass Compositor + { + Compositor(); + + SpriteVisual CreateSpriteVisual(Int32 brush); + ContainerVisual CreateContainerVisual(Int32 children); + } + + unsealed runtimeclass Visual + { + Compositor Compositor {get;}; + } + + unsealed runtimeclass ContainerVisual : Visual + { + Int32 Children {get;}; + } + + runtimeclass SpriteVisual : ContainerVisual + { + Int32 Brush {get;}; + } +} diff --git a/crates/tests/winrt/composable_client/Cargo.toml b/crates/tests/winrt/composable_client/Cargo.toml new file mode 100644 index 0000000000..9bd6f6041d --- /dev/null +++ b/crates/tests/winrt/composable_client/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "test_composable_client" +version = "0.0.0" +edition = "2021" +publish = false + +[lib] +doc = false +doctest = false + +[build-dependencies.windows-bindgen] +workspace = true + +[dependencies.windows-core] +workspace = true + +[dependencies.windows] +workspace = true +features = [ + "implement", + "Foundation", + "Win32_Foundation", +] + +# The build needs the output (.dll) of the component. This causes a warning about lack of linkage target. +# Cargo doesn't understand cdylib targets. https://github.com/rust-lang/cargo/issues/7825 +[dependencies.test_composable] +path = "../composable" + +[build-dependencies] +cc = "1.0" + +[build-dependencies.cppwinrt] +workspace = true diff --git a/crates/tests/winrt/composable_client/build.rs b/crates/tests/winrt/composable_client/build.rs new file mode 100644 index 0000000000..7240b6ff57 --- /dev/null +++ b/crates/tests/winrt/composable_client/build.rs @@ -0,0 +1,38 @@ +fn main() { + if !cfg!(target_env = "msvc") { + return; + } + + println!("cargo:rerun-if-changed=src/interop.cpp"); + + windows_bindgen::bindgen([ + "--in", + "../composable/metadata.winmd", + "--out", + "src/bindings.rs", + "--filter", + "test_composable", + "--config", + "no-bindgen-comment", + ]) + .unwrap(); + + let include = std::env::var("OUT_DIR").unwrap(); + + cppwinrt::cppwinrt([ + "-in", + "../composable/metadata.winmd", + "../../../libs/bindgen/default", + "-out", + &include, + ]) + .unwrap(); + + cc::Build::new() + .cpp(true) + .std("c++20") + .flag("/EHsc") + .file("src/interop.cpp") + .include(include) + .compile("interop"); +} diff --git a/crates/tests/winrt/composable_client/src/bindings.rs b/crates/tests/winrt/composable_client/src/bindings.rs new file mode 100644 index 0000000000..ac3805385d --- /dev/null +++ b/crates/tests/winrt/composable_client/src/bindings.rs @@ -0,0 +1,297 @@ +#![allow( + non_snake_case, + non_upper_case_globals, + non_camel_case_types, + dead_code, + clippy::all +)] +windows_core::imp::define_interface!( + ICompositor, + ICompositor_Vtbl, + 0xac7b49b8_e092_52ad_8456_48696a5a258e +); +impl windows_core::RuntimeType for ICompositor { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct ICompositor_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub CreateSpriteVisual: unsafe extern "system" fn( + *mut core::ffi::c_void, + i32, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub CreateContainerVisual: unsafe extern "system" fn( + *mut core::ffi::c_void, + i32, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, +} +windows_core::imp::define_interface!( + IContainerVisual, + IContainerVisual_Vtbl, + 0xb8accc46_3ff7_5a24_8247_f5a52e1f5a8d +); +impl windows_core::RuntimeType for IContainerVisual { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IContainerVisual_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Children: + unsafe extern "system" fn(*mut core::ffi::c_void, *mut i32) -> windows_core::HRESULT, +} +windows_core::imp::define_interface!( + IContainerVisualFactory, + IContainerVisualFactory_Vtbl, + 0x558b6180_1a65_5f01_8be2_2cc0b2034c0e +); +impl windows_core::RuntimeType for IContainerVisualFactory { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IContainerVisualFactory_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, +} +windows_core::imp::define_interface!( + ISpriteVisual, + ISpriteVisual_Vtbl, + 0x25f23ebe_4cd3_5349_b16d_d88c4d852ea1 +); +impl windows_core::RuntimeType for ISpriteVisual { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct ISpriteVisual_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Brush: unsafe extern "system" fn(*mut core::ffi::c_void, *mut i32) -> windows_core::HRESULT, +} +windows_core::imp::define_interface!( + IVisual, + IVisual_Vtbl, + 0xce89606a_5b03_5861_af26_9dced3aab7e6 +); +impl windows_core::RuntimeType for IVisual { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IVisual_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Compositor: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, +} +windows_core::imp::define_interface!( + IVisualFactory, + IVisualFactory_Vtbl, + 0x1974545d_259f_553c_8ea0_e505f897df81 +); +impl windows_core::RuntimeType for IVisualFactory { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +#[repr(C)] +pub struct IVisualFactory_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, +} +#[repr(transparent)] +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Compositor(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + Compositor, + windows_core::IUnknown, + windows_core::IInspectable +); +impl Compositor { + pub fn new() -> windows_core::Result { + Self::IActivationFactory(|f| f.ActivateInstance::()) + } + fn IActivationFactory< + R, + F: FnOnce(&windows_core::imp::IGenericFactory) -> windows_core::Result, + >( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache< + Compositor, + windows_core::imp::IGenericFactory, + > = windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } + pub fn CreateSpriteVisual(&self, brush: i32) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateSpriteVisual)( + windows_core::Interface::as_raw(this), + brush, + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn CreateContainerVisual(&self, children: i32) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateContainerVisual)( + windows_core::Interface::as_raw(this), + children, + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl windows_core::RuntimeType for Compositor { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for Compositor { + type Vtable = ICompositor_Vtbl; + const IID: windows_core::GUID = ::IID; +} +impl windows_core::RuntimeName for Compositor { + const NAME: &'static str = "test_composable.Compositor"; +} +unsafe impl Send for Compositor {} +unsafe impl Sync for Compositor {} +#[repr(transparent)] +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct ContainerVisual(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + ContainerVisual, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!(ContainerVisual, Visual); +impl ContainerVisual { + pub fn Children(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Children)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Compositor(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Compositor)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl windows_core::RuntimeType for ContainerVisual { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for ContainerVisual { + type Vtable = IContainerVisual_Vtbl; + const IID: windows_core::GUID = ::IID; +} +impl windows_core::RuntimeName for ContainerVisual { + const NAME: &'static str = "test_composable.ContainerVisual"; +} +unsafe impl Send for ContainerVisual {} +unsafe impl Sync for ContainerVisual {} +#[repr(transparent)] +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct SpriteVisual(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + SpriteVisual, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!(SpriteVisual, ContainerVisual, Visual); +impl SpriteVisual { + pub fn Children(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Children)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Brush(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Brush)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Compositor(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Compositor)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl windows_core::RuntimeType for SpriteVisual { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for SpriteVisual { + type Vtable = ISpriteVisual_Vtbl; + const IID: windows_core::GUID = ::IID; +} +impl windows_core::RuntimeName for SpriteVisual { + const NAME: &'static str = "test_composable.SpriteVisual"; +} +unsafe impl Send for SpriteVisual {} +unsafe impl Sync for SpriteVisual {} +#[repr(transparent)] +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Visual(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!(Visual, windows_core::IUnknown, windows_core::IInspectable); +impl Visual { + pub fn Compositor(&self) -> windows_core::Result { + let this = self; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Compositor)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl windows_core::RuntimeType for Visual { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for Visual { + type Vtable = IVisual_Vtbl; + const IID: windows_core::GUID = ::IID; +} +impl windows_core::RuntimeName for Visual { + const NAME: &'static str = "test_composable.Visual"; +} +unsafe impl Send for Visual {} +unsafe impl Sync for Visual {} diff --git a/crates/tests/winrt/composable_client/src/interop.cpp b/crates/tests/winrt/composable_client/src/interop.cpp new file mode 100644 index 0000000000..0bf8853759 --- /dev/null +++ b/crates/tests/winrt/composable_client/src/interop.cpp @@ -0,0 +1,34 @@ +#include +#include +#include "winrt/test_composable.h" + +using namespace winrt; +using namespace test_composable; + +void test() +{ + Compositor compositor; + + auto container = compositor.CreateContainerVisual(123); + assert(container.Children() == 123); + assert(container.Compositor() == compositor); + + auto sprite = compositor.CreateSpriteVisual(456); + assert(sprite.Brush() == 456); + assert(sprite.Children() == 456 * 2); + assert(sprite.Compositor() == compositor); +} + +extern "C" +{ + HRESULT __stdcall interop() noexcept + try + { + test(); + return S_OK; + } + catch (...) + { + return to_hresult(); + } +} diff --git a/crates/tests/winrt/composable_client/src/lib.rs b/crates/tests/winrt/composable_client/src/lib.rs new file mode 100644 index 0000000000..a825bb96d7 --- /dev/null +++ b/crates/tests/winrt/composable_client/src/lib.rs @@ -0,0 +1,28 @@ +#![cfg(target_env = "msvc")] +#![cfg(test)] + +mod bindings; +use bindings::*; +use windows::core::*; + +extern "system" { + fn interop() -> HRESULT; +} + +#[test] +fn test() -> Result<()> { + unsafe { interop().ok()? }; + + let compositor = Compositor::new()?; + + let container = compositor.CreateContainerVisual(123)?; + assert_eq!(container.Children()?, 123); + assert_eq!(container.Compositor()?, compositor); + + let sprite = compositor.CreateSpriteVisual(456)?; + assert_eq!(sprite.Brush()?, 456); + assert_eq!(sprite.Children()?, 456 * 2); + assert_eq!(sprite.Compositor()?, compositor); + + Ok(()) +} diff --git a/crates/tests/winrt/constructors_client/build.rs b/crates/tests/winrt/constructors_client/build.rs index f6765dbb7c..ac4cef14f2 100644 --- a/crates/tests/winrt/constructors_client/build.rs +++ b/crates/tests/winrt/constructors_client/build.rs @@ -22,7 +22,7 @@ fn main() { cppwinrt::cppwinrt([ "-in", "../constructors/metadata.winmd", - &format!("{}\\System32\\WinMetadata", env!("windir")), + "../../../libs/bindgen/default", "-out", &include, ])