From 17df7c55b708b3aab2a56bfbcb34c5f5c3668622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ralph=20K=C3=BCpper?= Date: Mon, 24 Aug 2026 14:10:01 +0200 Subject: [PATCH] feat(windows): complete WinUI 3 backend Lands #8722 with its four gate blockers resolved. Replaces the windows-winui scaffold with real WinUI 3 / Fluent rendering through a vendored Windows Reactor snapshot (microsoft/windows-rs 65066a7109c214f317ed66261cfb7518160b8aaf), upstream licenses preserved. The vendored tree moved from `crates/perry-ui-windows-winui/vendor/` to `third_party/windows-winui/`, and this was forced rather than chosen. Cargo's `is_excluded` is `!explicit_member && excluded`, and `explicit_member` matches any `members` entry that is a path PREFIX of the candidate -- so while the snapshot sat under a member directory, `exclude` lost and all 13 crates were real workspace members. Verified empirically: adding `exclude = ["crates/perry-ui-windows-winui/vendor"]` in place left cargo reporting 92 packages with all 13 still members; after the move it reports 79, matching the existing architecture baseline exactly, so no baseline refresh was needed. As members they entered the build graph, so `cargo test --workspace` compiled them and Perry's `[workspace.lints]` would have applied to upstream code. GC roots, the part that mattered: `Node` DOES hold JS values -- every widget callback is stored as a raw closure pointer via `js_nanbox_get_pointer` -- so it gets scanner coverage following #8713's pattern, not a verdict. `AppState` does not: `String`, two `f64` dimensions, an `i64` that indexes NODES rather than an address, two `Option<(f64,f64)>` and a `PresenterKind`, so it takes a `not_a_gc_pointer` verdict. Three further roots the census had not flagged are scanned too (`ON_ACTIVATE`, `ON_TERMINATE`, `PENDING_TIMERS`), and the scanner is armed on the Fluent path, where winui shadows `app_create` and would otherwise have left `perry-ui-windows`' own tables unscanned. The four oversized files are generated upstream bindings and are allowlisted as an immutable snapshot. `build_and_run.rs` was NOT allowlisted -- the block this PR added moved to `link/winui_assets.rs`, 2110 -> 1970. No version bump; the only root Cargo.toml change is the `exclude` entry. --- Cargo.lock | 179 +- Cargo.toml | 18 + changelog.d/8722-windows-winui.md | 25 + crates/perry-ui-windows-winui/Cargo.toml | 38 +- crates/perry-ui-windows-winui/src/app.rs | 330 + crates/perry-ui-windows-winui/src/gc.rs | 61 + crates/perry-ui-windows-winui/src/lib.rs | 86 +- crates/perry-ui-windows-winui/src/pointer.rs | 27 + crates/perry-ui-windows-winui/src/widgets.rs | 1407 + crates/perry-ui-windows-winui/src/winui.rs | 54 +- crates/perry-ui-windows/Cargo.toml | 5 + crates/perry-ui-windows/src/gc.rs | 2 +- crates/perry-ui-windows/src/lib.rs | 8 +- crates/perry-ui-windows/src/state.rs | 39 +- .../perry-ui-windows/src/widgets/textfield.rs | 6 +- .../src/commands/compile/library_search.rs | 5 +- .../commands/compile/link/build_and_run.rs | 11 + crates/perry/src/commands/compile/link/mod.rs | 1 + .../src/commands/compile/link/winui_assets.rs | 147 + docs/examples/ui/state/todo_app.ts | 2 +- docs/src/cli/flags.md | 2 +- docs/src/platforms/windows.md | 35 + scripts/check_file_size.sh | 21 + scripts/gc_runtime_root_holders.json | 6 + third_party/windows-winui/VENDORED.md | 18 + .../windows-collections/Cargo.toml | 23 + .../windows-collections/license-apache-2.0 | 201 + .../windows-collections/license-mit | 21 + .../windows-collections/readme.md | 46 + .../windows-collections/src/bindings.rs | 3094 ++ .../windows-collections/src/iterable.rs | 111 + .../windows-collections/src/key_value_pair.rs | 36 + .../windows-collections/src/lib.rs | 33 + .../windows-collections/src/map.rs | 194 + .../windows-collections/src/map_view.rs | 161 + .../windows-collections/src/observable_map.rs | 277 + .../src/observable_vector.rs | 295 + .../windows-collections/src/vector.rs | 224 + .../windows-collections/src/vector_view.rs | 153 + .../windows-winui/windows-core/Cargo.toml | 30 + .../windows-core/license-apache-2.0 | 201 + .../windows-winui/windows-core/license-mit | 21 + .../windows-winui/windows-core/readme.md | 7 + .../windows-core/src/agile_reference.rs | 40 + .../windows-winui/windows-core/src/array.rs | 157 + .../windows-winui/windows-core/src/as_impl.rs | 20 + .../windows-core/src/com_object.rs | 383 + .../windows-winui/windows-core/src/compose.rs | 62 + .../windows-winui/windows-core/src/event.rs | 142 + .../windows-core/src/event_revoker.rs | 59 + .../windows-winui/windows-core/src/guid.rs | 207 + .../windows-core/src/imp/agile.rs | 64 + .../windows-core/src/imp/array_proxy.rs | 38 + .../windows-core/src/imp/bindings.rs | 40 + .../windows-core/src/imp/can_into.rs | 5 + .../windows-core/src/imp/com_bindings.rs | 246 + .../windows-core/src/imp/delegate_box.rs | 92 + .../windows-core/src/imp/factory_cache.rs | 203 + .../windows-core/src/imp/generic_factory.rs | 33 + .../windows-core/src/imp/marshaler.rs | 268 + .../windows-winui/windows-core/src/imp/mod.rs | 122 + .../windows-core/src/imp/ref_count.rs | 33 + .../windows-core/src/imp/sha1.rs | 487 + .../windows-core/src/imp/weak_ref_count.rs | 329 + .../windows-core/src/imp/windows.rs | 11 + .../windows-core/src/implement_macro.rs | 1208 + .../windows-winui/windows-core/src/in_ref.rs | 89 + .../windows-core/src/inspectable.rs | 117 + .../windows-core/src/interface.rs | 329 + .../windows-core/src/interface_macro.rs | 405 + .../windows-winui/windows-core/src/lib.rs | 126 + .../windows-core/src/out_param.rs | 63 + .../windows-winui/windows-core/src/out_ref.rs | 37 + .../windows-winui/windows-core/src/param.rs | 76 + .../windows-core/src/param_value.rs | 24 + .../windows-core/src/resources.rs | 54 + .../windows-core/src/runtime_name.rs | 8 + .../windows-core/src/runtime_type.rs | 32 + .../windows-core/src/scoped_interface.rs | 41 + .../windows-winui/windows-core/src/type.rs | 181 + .../windows-winui/windows-core/src/unknown.rs | 186 + .../windows-winui/windows-core/src/weak.rs | 28 + .../windows-winui/windows-core/src/windows.rs | 15 + .../windows-core/windows-core.natvis | 18 + .../windows-winui/windows-future/Cargo.toml | 22 + .../windows-future/license-apache-2.0 | 201 + .../windows-winui/windows-future/license-mit | 21 + .../windows-winui/windows-future/readme.md | 31 + .../windows-winui/windows-future/src/async.rs | 170 + .../windows-future/src/async_ready.rs | 252 + .../windows-future/src/async_spawn.rs | 346 + .../windows-future/src/bindings.rs | 2015 ++ .../windows-future/src/bindings_impl.rs | 20 + .../windows-future/src/future.rs | 121 + .../windows-winui/windows-future/src/join.rs | 29 + .../windows-winui/windows-future/src/lib.rs | 31 + .../windows-future/src/waiter.rs | 89 + .../windows-winui/windows-future/src/when.rs | 41 + .../windows-implement/Cargo.toml | 24 + .../windows-implement/license-apache-2.0 | 201 + .../windows-implement/license-mit | 21 + .../windows-winui/windows-implement/readme.md | 3 + .../windows-implement/src/gen.rs | 647 + .../windows-implement/src/lib.rs | 471 + .../windows-implement/src/tests.rs | 286 + .../windows-interface/Cargo.toml | 24 + .../windows-interface/license-apache-2.0 | 201 + .../windows-interface/license-mit | 21 + .../windows-winui/windows-interface/readme.md | 3 + .../windows-interface/src/gen.rs | 519 + .../windows-interface/src/guid.rs | 101 + .../windows-interface/src/lib.rs | 248 + .../windows-interface/src/tests.rs | 233 + .../windows-winui/windows-link/Cargo.toml | 11 + .../windows-link/license-apache-2.0 | 201 + .../windows-winui/windows-link/license-mit | 21 + .../windows-winui/windows-link/readme.md | 37 + .../windows-winui/windows-link/src/lib.rs | 63 + .../windows-winui/windows-numerics/Cargo.toml | 21 + .../windows-numerics/license-apache-2.0 | 201 + .../windows-numerics/license-mit | 21 + .../windows-winui/windows-numerics/readme.md | 7 + .../windows-numerics/src/bindings.rs | 99 + .../windows-winui/windows-numerics/src/lib.rs | 13 + .../windows-numerics/src/matrix3x2.rs | 195 + .../windows-numerics/src/matrix4x4.rs | 232 + .../windows-numerics/src/vector2.rs | 216 + .../windows-numerics/src/vector3.rs | 254 + .../windows-numerics/src/vector4.rs | 266 + .../windows-winui/windows-reactor/Cargo.toml | 24 + .../windows-winui/windows-reactor/build.rs | 242 + .../windows-reactor/license-apache-2.0 | 201 + .../windows-winui/windows-reactor/license-mit | 21 + .../windows-winui/windows-reactor/readme.md | 1 + .../windows-winui/windows-reactor/src/app.rs | 350 + .../windows-reactor/src/app_shim.rs | 73 + .../windows-reactor/src/bindings.rs | 26441 ++++++++++++++++ .../windows-reactor/src/core/accessibility.rs | 40 + .../windows-reactor/src/core/animation.rs | 176 + .../windows-reactor/src/core/backend.rs | 1185 + .../windows-reactor/src/core/callback.rs | 127 + .../windows-reactor/src/core/component.rs | 47 + .../src/core/component_element.rs | 158 + .../windows-reactor/src/core/context.rs | 264 + .../windows-reactor/src/core/custom.rs | 84 + .../windows-reactor/src/core/dispatcher.rs | 275 + .../windows-reactor/src/core/element.rs | 456 + .../src/core/error_boundary.rs | 86 + .../windows-reactor/src/core/geometry.rs | 93 + .../windows-reactor/src/core/into_elements.rs | 62 + .../windows-reactor/src/core/keyboard.rs | 137 + .../windows-reactor/src/core/mod.rs | 58 + .../windows-reactor/src/core/modifiers.rs | 175 + .../windows-reactor/src/core/pointer.rs | 37 + .../windows-reactor/src/core/prop_binding.rs | 51 + .../windows-reactor/src/core/rc_fn.rs | 52 + .../windows-reactor/src/core/reconciler.rs | 870 + .../src/core/reconciler/child.rs | 669 + .../src/core/reconciler/diff_helpers.rs | 98 + .../src/core/reconciler/templated.rs | 340 + .../src/core/reconciler/widget_dispatch.rs | 484 + .../src/core/reconciler/wrappers.rs | 258 + .../src/core/render_context.rs | 870 + .../windows-reactor/src/core/render_host.rs | 444 + .../windows-reactor/src/core/resource.rs | 298 + .../windows-reactor/src/core/rich_text.rs | 114 + .../src/core/templated_list.rs | 313 + .../windows-reactor/src/core/theme.rs | 216 + .../windows-reactor/src/core/tooltip.rs | 61 + .../windows-reactor/src/core/widget.rs | 33 + .../src/core/widgets/auto_suggest_box.rs | 113 + .../src/core/widgets/border.rs | 101 + .../src/core/widgets/breadcrumb_bar.rs | 43 + .../src/core/widgets/button.rs | 198 + .../src/core/widgets/calendar_date_picker.rs | 88 + .../src/core/widgets/calendar_view.rs | 67 + .../src/core/widgets/canvas.rs | 41 + .../src/core/widgets/check_box.rs | 68 + .../src/core/widgets/color_picker.rs | 111 + .../src/core/widgets/combo_box.rs | 89 + .../src/core/widgets/command_bar.rs | 132 + .../src/core/widgets/content_dialog.rs | 140 + .../src/core/widgets/date_picker.rs | 85 + .../src/core/widgets/drop_down_button.rs | 93 + .../src/core/widgets/expander.rs | 88 + .../src/core/widgets/flyout.rs | 27 + .../windows-reactor/src/core/widgets/grid.rs | 70 + .../src/core/widgets/hyperlink_button.rs | 56 + .../windows-reactor/src/core/widgets/icon.rs | 81 + .../windows-reactor/src/core/widgets/image.rs | 47 + .../src/core/widgets/info_badge.rs | 27 + .../src/core/widgets/info_bar.rs | 116 + .../src/core/widgets/list_box.rs | 70 + .../src/core/widgets/menu_bar.rs | 100 + .../windows-reactor/src/core/widgets/mod.rs | 91 + .../src/core/widgets/navigation_view.rs | 286 + .../src/core/widgets/number_box.rs | 82 + .../src/core/widgets/password_box.rs | 108 + .../src/core/widgets/person_picture.rs | 38 + .../windows-reactor/src/core/widgets/pivot.rs | 77 + .../src/core/widgets/progress_bar.rs | 57 + .../src/core/widgets/progress_ring.rs | 53 + .../src/core/widgets/radio_button.rs | 67 + .../src/core/widgets/radio_buttons.rs | 75 + .../src/core/widgets/rating_control.rs | 84 + .../src/core/widgets/relative_panel.rs | 53 + .../src/core/widgets/repeat_button.rs | 75 + .../src/core/widgets/rich_edit_box.rs | 77 + .../src/core/widgets/scroll_view.rs | 101 + .../src/core/widgets/scroll_viewer.rs | 64 + .../src/core/widgets/selector_bar.rs | 74 + .../windows-reactor/src/core/widgets/shape.rs | 124 + .../src/core/widgets/slider.rs | 102 + .../src/core/widgets/split_button.rs | 59 + .../src/core/widgets/split_view.rs | 129 + .../src/core/widgets/stack_panel.rs | 61 + .../src/core/widgets/tab_view.rs | 93 + .../src/core/widgets/teaching_tip.rs | 146 + .../src/core/widgets/text_block.rs | 103 + .../src/core/widgets/text_box.rs | 98 + .../src/core/widgets/time_picker.rs | 80 + .../src/core/widgets/title_bar.rs | 117 + .../src/core/widgets/toggle_button.rs | 64 + .../src/core/widgets/toggle_switch.rs | 69 + .../src/core/widgets/tree_view.rs | 109 + .../src/core/widgets/viewbox.rs | 51 + .../windows-reactor/src/core/window.rs | 13 + .../windows-reactor/src/diagnostics.rs | 114 + .../windows-reactor/src/dsl/factories.rs | 3 + .../windows-reactor/src/dsl/mod.rs | 5 + .../windows-reactor/src/dsl/modifiers.rs | 607 + .../windows-winui/windows-reactor/src/lib.rs | 53 + .../src/winui/backend/convert.rs | 377 + .../windows-reactor/src/winui/backend/diag.rs | 37 + .../windows-reactor/src/winui/backend/mod.rs | 4246 +++ .../windows-reactor/src/winui/dispatcher.rs | 87 + .../windows-reactor/src/winui/hooks.rs | 80 + .../windows-reactor/src/winui/host.rs | 600 + .../windows-reactor/src/winui/mod.rs | 13 + .../src/winui/template_cache.rs | 38 + .../windows-reference/Cargo.toml | 21 + .../windows-reference/license-apache-2.0 | 201 + .../windows-reference/license-mit | 21 + .../windows-winui/windows-reference/readme.md | 23 + .../windows-reference/src/bindings.rs | 1209 + .../windows-reference/src/lib.rs | 18 + .../windows-reference/src/reference.rs | 300 + .../windows-winui/windows-result/Cargo.toml | 21 + .../windows-result/license-apache-2.0 | 201 + .../windows-winui/windows-result/license-mit | 21 + .../windows-winui/windows-result/readme.md | 32 + .../windows-result/src/bindings.rs | 83 + .../windows-winui/windows-result/src/bool.rs | 90 + .../windows-winui/windows-result/src/bstr.rs | 40 + .../windows-winui/windows-result/src/com.rs | 54 + .../windows-winui/windows-result/src/error.rs | 377 + .../windows-result/src/hresult.rs | 147 + .../windows-winui/windows-result/src/lib.rs | 50 + .../windows-result/src/ntstatus.rs | 71 + .../windows-result/src/rpc_status.rs | 57 + .../windows-result/src/strings.rs | 29 + .../windows-result/src/win32_error.rs | 76 + .../windows-result/windows-result.natvis | 21 + .../windows-winui/windows-strings/Cargo.toml | 21 + .../windows-strings/license-apache-2.0 | 201 + .../windows-winui/windows-strings/license-mit | 21 + .../windows-winui/windows-strings/readme.md | 34 + .../windows-strings/src/bindings.rs | 11 + .../windows-winui/windows-strings/src/bstr.rs | 160 + .../windows-strings/src/decode.rs | 59 + .../windows-strings/src/hstring.rs | 387 + .../windows-strings/src/hstring_builder.rs | 108 + .../windows-strings/src/hstring_header.rs | 127 + .../windows-winui/windows-strings/src/lib.rs | 67 + .../windows-strings/src/literals.rs | 165 + .../windows-strings/src/pcstr.rs | 64 + .../windows-strings/src/pcwstr.rs | 105 + .../windows-winui/windows-strings/src/pstr.rs | 64 + .../windows-strings/src/pwstr.rs | 88 + .../windows-strings/src/ref_count.rs | 27 + .../windows-strings/windows-strings.natvis | 62 + .../windows-threading/Cargo.toml | 24 + .../examples/threading_bench.rs | 169 + .../windows-threading/license-apache-2.0 | 201 + .../windows-threading/license-mit | 21 + .../windows-winui/windows-threading/readme.md | 116 + .../windows-threading/src/bindings.rs | 60 + .../windows-threading/src/lib.rs | 119 + .../windows-threading/src/pool.rs | 141 + .../windows-winui/windows-time/Cargo.toml | 20 + .../windows-time/license-apache-2.0 | 201 + .../windows-winui/windows-time/license-mit | 21 + .../windows-winui/windows-time/readme.md | 7 + .../windows-time/src/bindings.rs | 28 + .../windows-time/src/datetime.rs | 313 + .../windows-winui/windows-time/src/lib.rs | 12 + .../windows-time/src/timespan.rs | 405 + 297 files changed, 75766 insertions(+), 190 deletions(-) create mode 100644 changelog.d/8722-windows-winui.md create mode 100644 crates/perry-ui-windows-winui/src/app.rs create mode 100644 crates/perry-ui-windows-winui/src/gc.rs create mode 100644 crates/perry-ui-windows-winui/src/pointer.rs create mode 100644 crates/perry-ui-windows-winui/src/widgets.rs create mode 100644 crates/perry/src/commands/compile/link/winui_assets.rs create mode 100644 third_party/windows-winui/VENDORED.md create mode 100644 third_party/windows-winui/windows-collections/Cargo.toml create mode 100644 third_party/windows-winui/windows-collections/license-apache-2.0 create mode 100644 third_party/windows-winui/windows-collections/license-mit create mode 100644 third_party/windows-winui/windows-collections/readme.md create mode 100644 third_party/windows-winui/windows-collections/src/bindings.rs create mode 100644 third_party/windows-winui/windows-collections/src/iterable.rs create mode 100644 third_party/windows-winui/windows-collections/src/key_value_pair.rs create mode 100644 third_party/windows-winui/windows-collections/src/lib.rs create mode 100644 third_party/windows-winui/windows-collections/src/map.rs create mode 100644 third_party/windows-winui/windows-collections/src/map_view.rs create mode 100644 third_party/windows-winui/windows-collections/src/observable_map.rs create mode 100644 third_party/windows-winui/windows-collections/src/observable_vector.rs create mode 100644 third_party/windows-winui/windows-collections/src/vector.rs create mode 100644 third_party/windows-winui/windows-collections/src/vector_view.rs create mode 100644 third_party/windows-winui/windows-core/Cargo.toml create mode 100644 third_party/windows-winui/windows-core/license-apache-2.0 create mode 100644 third_party/windows-winui/windows-core/license-mit create mode 100644 third_party/windows-winui/windows-core/readme.md create mode 100644 third_party/windows-winui/windows-core/src/agile_reference.rs create mode 100644 third_party/windows-winui/windows-core/src/array.rs create mode 100644 third_party/windows-winui/windows-core/src/as_impl.rs create mode 100644 third_party/windows-winui/windows-core/src/com_object.rs create mode 100644 third_party/windows-winui/windows-core/src/compose.rs create mode 100644 third_party/windows-winui/windows-core/src/event.rs create mode 100644 third_party/windows-winui/windows-core/src/event_revoker.rs create mode 100644 third_party/windows-winui/windows-core/src/guid.rs create mode 100644 third_party/windows-winui/windows-core/src/imp/agile.rs create mode 100644 third_party/windows-winui/windows-core/src/imp/array_proxy.rs create mode 100644 third_party/windows-winui/windows-core/src/imp/bindings.rs create mode 100644 third_party/windows-winui/windows-core/src/imp/can_into.rs create mode 100644 third_party/windows-winui/windows-core/src/imp/com_bindings.rs create mode 100644 third_party/windows-winui/windows-core/src/imp/delegate_box.rs create mode 100644 third_party/windows-winui/windows-core/src/imp/factory_cache.rs create mode 100644 third_party/windows-winui/windows-core/src/imp/generic_factory.rs create mode 100644 third_party/windows-winui/windows-core/src/imp/marshaler.rs create mode 100644 third_party/windows-winui/windows-core/src/imp/mod.rs create mode 100644 third_party/windows-winui/windows-core/src/imp/ref_count.rs create mode 100644 third_party/windows-winui/windows-core/src/imp/sha1.rs create mode 100644 third_party/windows-winui/windows-core/src/imp/weak_ref_count.rs create mode 100644 third_party/windows-winui/windows-core/src/imp/windows.rs create mode 100644 third_party/windows-winui/windows-core/src/implement_macro.rs create mode 100644 third_party/windows-winui/windows-core/src/in_ref.rs create mode 100644 third_party/windows-winui/windows-core/src/inspectable.rs create mode 100644 third_party/windows-winui/windows-core/src/interface.rs create mode 100644 third_party/windows-winui/windows-core/src/interface_macro.rs create mode 100644 third_party/windows-winui/windows-core/src/lib.rs create mode 100644 third_party/windows-winui/windows-core/src/out_param.rs create mode 100644 third_party/windows-winui/windows-core/src/out_ref.rs create mode 100644 third_party/windows-winui/windows-core/src/param.rs create mode 100644 third_party/windows-winui/windows-core/src/param_value.rs create mode 100644 third_party/windows-winui/windows-core/src/resources.rs create mode 100644 third_party/windows-winui/windows-core/src/runtime_name.rs create mode 100644 third_party/windows-winui/windows-core/src/runtime_type.rs create mode 100644 third_party/windows-winui/windows-core/src/scoped_interface.rs create mode 100644 third_party/windows-winui/windows-core/src/type.rs create mode 100644 third_party/windows-winui/windows-core/src/unknown.rs create mode 100644 third_party/windows-winui/windows-core/src/weak.rs create mode 100644 third_party/windows-winui/windows-core/src/windows.rs create mode 100644 third_party/windows-winui/windows-core/windows-core.natvis create mode 100644 third_party/windows-winui/windows-future/Cargo.toml create mode 100644 third_party/windows-winui/windows-future/license-apache-2.0 create mode 100644 third_party/windows-winui/windows-future/license-mit create mode 100644 third_party/windows-winui/windows-future/readme.md create mode 100644 third_party/windows-winui/windows-future/src/async.rs create mode 100644 third_party/windows-winui/windows-future/src/async_ready.rs create mode 100644 third_party/windows-winui/windows-future/src/async_spawn.rs create mode 100644 third_party/windows-winui/windows-future/src/bindings.rs create mode 100644 third_party/windows-winui/windows-future/src/bindings_impl.rs create mode 100644 third_party/windows-winui/windows-future/src/future.rs create mode 100644 third_party/windows-winui/windows-future/src/join.rs create mode 100644 third_party/windows-winui/windows-future/src/lib.rs create mode 100644 third_party/windows-winui/windows-future/src/waiter.rs create mode 100644 third_party/windows-winui/windows-future/src/when.rs create mode 100644 third_party/windows-winui/windows-implement/Cargo.toml create mode 100644 third_party/windows-winui/windows-implement/license-apache-2.0 create mode 100644 third_party/windows-winui/windows-implement/license-mit create mode 100644 third_party/windows-winui/windows-implement/readme.md create mode 100644 third_party/windows-winui/windows-implement/src/gen.rs create mode 100644 third_party/windows-winui/windows-implement/src/lib.rs create mode 100644 third_party/windows-winui/windows-implement/src/tests.rs create mode 100644 third_party/windows-winui/windows-interface/Cargo.toml create mode 100644 third_party/windows-winui/windows-interface/license-apache-2.0 create mode 100644 third_party/windows-winui/windows-interface/license-mit create mode 100644 third_party/windows-winui/windows-interface/readme.md create mode 100644 third_party/windows-winui/windows-interface/src/gen.rs create mode 100644 third_party/windows-winui/windows-interface/src/guid.rs create mode 100644 third_party/windows-winui/windows-interface/src/lib.rs create mode 100644 third_party/windows-winui/windows-interface/src/tests.rs create mode 100644 third_party/windows-winui/windows-link/Cargo.toml create mode 100644 third_party/windows-winui/windows-link/license-apache-2.0 create mode 100644 third_party/windows-winui/windows-link/license-mit create mode 100644 third_party/windows-winui/windows-link/readme.md create mode 100644 third_party/windows-winui/windows-link/src/lib.rs create mode 100644 third_party/windows-winui/windows-numerics/Cargo.toml create mode 100644 third_party/windows-winui/windows-numerics/license-apache-2.0 create mode 100644 third_party/windows-winui/windows-numerics/license-mit create mode 100644 third_party/windows-winui/windows-numerics/readme.md create mode 100644 third_party/windows-winui/windows-numerics/src/bindings.rs create mode 100644 third_party/windows-winui/windows-numerics/src/lib.rs create mode 100644 third_party/windows-winui/windows-numerics/src/matrix3x2.rs create mode 100644 third_party/windows-winui/windows-numerics/src/matrix4x4.rs create mode 100644 third_party/windows-winui/windows-numerics/src/vector2.rs create mode 100644 third_party/windows-winui/windows-numerics/src/vector3.rs create mode 100644 third_party/windows-winui/windows-numerics/src/vector4.rs create mode 100644 third_party/windows-winui/windows-reactor/Cargo.toml create mode 100644 third_party/windows-winui/windows-reactor/build.rs create mode 100644 third_party/windows-winui/windows-reactor/license-apache-2.0 create mode 100644 third_party/windows-winui/windows-reactor/license-mit create mode 100644 third_party/windows-winui/windows-reactor/readme.md create mode 100644 third_party/windows-winui/windows-reactor/src/app.rs create mode 100644 third_party/windows-winui/windows-reactor/src/app_shim.rs create mode 100644 third_party/windows-winui/windows-reactor/src/bindings.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/accessibility.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/animation.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/backend.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/callback.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/component.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/component_element.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/context.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/custom.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/dispatcher.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/element.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/error_boundary.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/geometry.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/into_elements.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/keyboard.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/mod.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/modifiers.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/pointer.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/prop_binding.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/rc_fn.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/reconciler.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/reconciler/child.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/reconciler/diff_helpers.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/reconciler/templated.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/reconciler/widget_dispatch.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/reconciler/wrappers.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/render_context.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/render_host.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/resource.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/rich_text.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/templated_list.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/theme.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/tooltip.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widget.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/auto_suggest_box.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/border.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/breadcrumb_bar.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/button.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/calendar_date_picker.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/calendar_view.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/canvas.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/check_box.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/color_picker.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/combo_box.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/command_bar.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/content_dialog.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/date_picker.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/drop_down_button.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/expander.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/flyout.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/grid.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/hyperlink_button.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/icon.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/image.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/info_badge.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/info_bar.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/list_box.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/menu_bar.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/mod.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/navigation_view.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/number_box.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/password_box.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/person_picture.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/pivot.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/progress_bar.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/progress_ring.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/radio_button.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/radio_buttons.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/rating_control.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/relative_panel.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/repeat_button.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/rich_edit_box.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/scroll_view.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/scroll_viewer.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/selector_bar.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/shape.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/slider.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/split_button.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/split_view.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/stack_panel.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/tab_view.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/teaching_tip.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/text_block.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/text_box.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/time_picker.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/title_bar.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/toggle_button.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/toggle_switch.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/tree_view.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/widgets/viewbox.rs create mode 100644 third_party/windows-winui/windows-reactor/src/core/window.rs create mode 100644 third_party/windows-winui/windows-reactor/src/diagnostics.rs create mode 100644 third_party/windows-winui/windows-reactor/src/dsl/factories.rs create mode 100644 third_party/windows-winui/windows-reactor/src/dsl/mod.rs create mode 100644 third_party/windows-winui/windows-reactor/src/dsl/modifiers.rs create mode 100644 third_party/windows-winui/windows-reactor/src/lib.rs create mode 100644 third_party/windows-winui/windows-reactor/src/winui/backend/convert.rs create mode 100644 third_party/windows-winui/windows-reactor/src/winui/backend/diag.rs create mode 100644 third_party/windows-winui/windows-reactor/src/winui/backend/mod.rs create mode 100644 third_party/windows-winui/windows-reactor/src/winui/dispatcher.rs create mode 100644 third_party/windows-winui/windows-reactor/src/winui/hooks.rs create mode 100644 third_party/windows-winui/windows-reactor/src/winui/host.rs create mode 100644 third_party/windows-winui/windows-reactor/src/winui/mod.rs create mode 100644 third_party/windows-winui/windows-reactor/src/winui/template_cache.rs create mode 100644 third_party/windows-winui/windows-reference/Cargo.toml create mode 100644 third_party/windows-winui/windows-reference/license-apache-2.0 create mode 100644 third_party/windows-winui/windows-reference/license-mit create mode 100644 third_party/windows-winui/windows-reference/readme.md create mode 100644 third_party/windows-winui/windows-reference/src/bindings.rs create mode 100644 third_party/windows-winui/windows-reference/src/lib.rs create mode 100644 third_party/windows-winui/windows-reference/src/reference.rs create mode 100644 third_party/windows-winui/windows-result/Cargo.toml create mode 100644 third_party/windows-winui/windows-result/license-apache-2.0 create mode 100644 third_party/windows-winui/windows-result/license-mit create mode 100644 third_party/windows-winui/windows-result/readme.md create mode 100644 third_party/windows-winui/windows-result/src/bindings.rs create mode 100644 third_party/windows-winui/windows-result/src/bool.rs create mode 100644 third_party/windows-winui/windows-result/src/bstr.rs create mode 100644 third_party/windows-winui/windows-result/src/com.rs create mode 100644 third_party/windows-winui/windows-result/src/error.rs create mode 100644 third_party/windows-winui/windows-result/src/hresult.rs create mode 100644 third_party/windows-winui/windows-result/src/lib.rs create mode 100644 third_party/windows-winui/windows-result/src/ntstatus.rs create mode 100644 third_party/windows-winui/windows-result/src/rpc_status.rs create mode 100644 third_party/windows-winui/windows-result/src/strings.rs create mode 100644 third_party/windows-winui/windows-result/src/win32_error.rs create mode 100644 third_party/windows-winui/windows-result/windows-result.natvis create mode 100644 third_party/windows-winui/windows-strings/Cargo.toml create mode 100644 third_party/windows-winui/windows-strings/license-apache-2.0 create mode 100644 third_party/windows-winui/windows-strings/license-mit create mode 100644 third_party/windows-winui/windows-strings/readme.md create mode 100644 third_party/windows-winui/windows-strings/src/bindings.rs create mode 100644 third_party/windows-winui/windows-strings/src/bstr.rs create mode 100644 third_party/windows-winui/windows-strings/src/decode.rs create mode 100644 third_party/windows-winui/windows-strings/src/hstring.rs create mode 100644 third_party/windows-winui/windows-strings/src/hstring_builder.rs create mode 100644 third_party/windows-winui/windows-strings/src/hstring_header.rs create mode 100644 third_party/windows-winui/windows-strings/src/lib.rs create mode 100644 third_party/windows-winui/windows-strings/src/literals.rs create mode 100644 third_party/windows-winui/windows-strings/src/pcstr.rs create mode 100644 third_party/windows-winui/windows-strings/src/pcwstr.rs create mode 100644 third_party/windows-winui/windows-strings/src/pstr.rs create mode 100644 third_party/windows-winui/windows-strings/src/pwstr.rs create mode 100644 third_party/windows-winui/windows-strings/src/ref_count.rs create mode 100644 third_party/windows-winui/windows-strings/windows-strings.natvis create mode 100644 third_party/windows-winui/windows-threading/Cargo.toml create mode 100644 third_party/windows-winui/windows-threading/examples/threading_bench.rs create mode 100644 third_party/windows-winui/windows-threading/license-apache-2.0 create mode 100644 third_party/windows-winui/windows-threading/license-mit create mode 100644 third_party/windows-winui/windows-threading/readme.md create mode 100644 third_party/windows-winui/windows-threading/src/bindings.rs create mode 100644 third_party/windows-winui/windows-threading/src/lib.rs create mode 100644 third_party/windows-winui/windows-threading/src/pool.rs create mode 100644 third_party/windows-winui/windows-time/Cargo.toml create mode 100644 third_party/windows-winui/windows-time/license-apache-2.0 create mode 100644 third_party/windows-winui/windows-time/license-mit create mode 100644 third_party/windows-winui/windows-time/readme.md create mode 100644 third_party/windows-winui/windows-time/src/bindings.rs create mode 100644 third_party/windows-winui/windows-time/src/datetime.rs create mode 100644 third_party/windows-winui/windows-time/src/lib.rs create mode 100644 third_party/windows-winui/windows-time/src/timespan.rs diff --git a/Cargo.lock b/Cargo.lock index 6c8a98c010..4645cbc6ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1112,7 +1112,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link", + "windows-link 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3408,7 +3408,7 @@ checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd" dependencies = [ "cfg-if", "libc", - "windows-link", + "windows-link 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3571,7 +3571,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.62.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4006,7 +4006,7 @@ dependencies = [ "socket2", "widestring", "windows-registry", - "windows-result", + "windows-result 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "windows-sys 0.61.2", ] @@ -4146,7 +4146,7 @@ dependencies = [ "simd_cesu8", "thiserror 2.0.18", "walkdir", - "windows-link", + "windows-link 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -4394,7 +4394,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link", + "windows-link 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -5507,7 +5507,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-link", + "windows-link 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -6630,14 +6630,20 @@ dependencies = [ "webview2-com", "windows 0.35.0", "windows 0.62.2", - "windows-core", + "windows-core 0.62.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "perry-ui-windows-winui" version = "0.5.1519" dependencies = [ + "base64 0.22.1", + "libc", + "perry-ffi", + "perry-runtime", "perry-ui-windows", + "windows 0.62.2", + "windows-reactor", ] [[package]] @@ -10382,7 +10388,7 @@ dependencies = [ "webview2-com-macros", "webview2-com-sys", "windows 0.62.2", - "windows-core", + "windows-core 0.62.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -10404,7 +10410,7 @@ checksum = "b3a07132775117d6065853d9d1178157b8c90e228de47129d6bce2c7edebedfb" dependencies = [ "thiserror 2.0.18", "windows 0.62.2", - "windows-core", + "windows-core 0.62.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -10484,10 +10490,17 @@ version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ - "windows-collections", - "windows-core", - "windows-future", - "windows-numerics", + "windows-collections 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "windows-core 0.62.2 (registry+https://github.com/rust-lang/crates.io-index)", + "windows-future 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "windows-numerics 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +dependencies = [ + "windows-core 0.62.2", ] [[package]] @@ -10496,7 +10509,18 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-core", + "windows-core 0.62.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +dependencies = [ + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] @@ -10505,11 +10529,20 @@ version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", + "windows-implement 0.60.2 (registry+https://github.com/rust-lang/crates.io-index)", + "windows-interface 0.59.3 (registry+https://github.com/rust-lang/crates.io-index)", + "windows-link 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "windows-result 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "windows-strings 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] @@ -10518,9 +10551,18 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "windows-core", - "windows-link", - "windows-threading", + "windows-core 0.62.2 (registry+https://github.com/rust-lang/crates.io-index)", + "windows-link 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "windows-threading 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -10534,6 +10576,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "windows-interface" +version = "0.59.3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "windows-interface" version = "0.59.3" @@ -10545,20 +10596,54 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "windows-link" +version = "0.2.1" + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.3.1" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + [[package]] name = "windows-numerics" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-core", - "windows-link", + "windows-core 0.62.2 (registry+https://github.com/rust-lang/crates.io-index)", + "windows-link 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "windows-reactor" +version = "0.0.0" +dependencies = [ + "rustc-hash 2.1.2", + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", + "windows-reference", + "windows-threading 0.2.1", + "windows-time", +] + +[[package]] +name = "windows-reference" +version = "0.1.0" +dependencies = [ + "windows-core 0.62.2", + "windows-time", ] [[package]] @@ -10567,9 +10652,16 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ - "windows-link", - "windows-result", - "windows-strings", + "windows-link 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "windows-result 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "windows-strings 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +dependencies = [ + "windows-link 0.2.1", ] [[package]] @@ -10578,7 +10670,14 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link", + "windows-link 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +dependencies = [ + "windows-link 0.2.1", ] [[package]] @@ -10587,7 +10686,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link", + "windows-link 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -10641,7 +10740,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link", + "windows-link 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -10696,7 +10795,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link", + "windows-link 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -10707,13 +10806,27 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-threading" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" dependencies = [ - "windows-link", + "windows-link 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "windows-time" +version = "0.1.0" +dependencies = [ + "windows-core 0.62.2", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 655dd0fc4f..e890c61a5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,6 +81,24 @@ members = [ "crates/perry-stdlib-static", "docs/examples/_fixtures/native-libraries/my-bindings", ] +# Vendored upstream windows-rs / Windows Reactor sources used by the opt-in +# WinUI 3 backend (`third_party/windows-winui/`, see its VENDORED.md). They are +# PATH DEPENDENCIES of `perry-ui-windows-winui`, not Perry crates: excluding +# them keeps them out of the workspace member set, so `cargo test --workspace` +# never compiles them and Perry's `[workspace.lints]` policy is never applied +# to third-party code. `perry-ui-windows-winui` still resolves them normally +# through its `path = "../../third_party/..."` dependency edge. +# +# They live OUTSIDE `crates/` on purpose. Cargo's `exclude` loses to an +# explicit `members` entry that is a path PREFIX of the excluded directory +# (`is_excluded` returns `!explicit_member && excluded`), so while these sat in +# `crates/perry-ui-windows-winui/vendor/` they were implicit members and NO +# `exclude` entry could remove them. Keeping them out of `crates/` also keeps +# them out of the `crates/perry-ui*` globs used by the GC/lint census gates. +exclude = [ + "third_party/windows-winui", +] + # Build only the host-independent product set by default. # Platform UI manifests keep their source and dependencies behind target cfgs. # Build each platform package explicitly for its applicable target. diff --git a/changelog.d/8722-windows-winui.md b/changelog.d/8722-windows-winui.md new file mode 100644 index 0000000000..bfe5357134 --- /dev/null +++ b/changelog.d/8722-windows-winui.md @@ -0,0 +1,25 @@ +`--target windows-winui` now renders Perry's core UI controls with native WinUI 3 / Fluent controls and Mica chrome through Windows Reactor. The compiler deploys the required unpackaged Windows App SDK bootstrap assets automatically, while the default `--target windows` backend remains unchanged. + +The vendored upstream `windows-rs` / Windows Reactor snapshot now lives in +`third_party/windows-winui/` (outside `crates/`) and is listed in +`[workspace] exclude`, so it is not a workspace member: `cargo test +--workspace` no longer compiles it and Perry's `[workspace.lints]` policy is +no longer applied to third-party code. It could not stay under +`crates/perry-ui-windows-winui/vendor/` — Cargo's `exclude` loses to an +explicit `members` entry that is a path prefix of the excluded directory, so +while it sat there the 13 vendored crates were implicit workspace members and +no `exclude` entry could remove them. `perry-ui-windows-winui` still resolves +them through a normal `path` dependency edge, and the only `Cargo.lock` change +is the loss of three dev-dependency edges that belong to the vendored crates. + +The WinUI backend now registers a GC root scanner +(`perry-ui-windows-winui/src/gc.rs`, following #8713). Every persistent +JavaScript callback it stores is a raw closure pointer unboxed by +`js_nanbox_get_pointer`, so `widgets::NODES` (the generic `on_click` plus the +`Button`/`TextField`/`SecureField`/`Toggle`/`Slider` handlers) and +`app::{ON_ACTIVATE, ON_TERMINATE, PENDING_TIMERS}` are GC roots that an +evacuating collection has to rewrite. Registration also chains +`perry-ui-windows`' scanner, which the Fluent path previously never armed +because it shadows `app_create`, and reaches this crate's own `#[path]`-included +copy of `state.rs`. The per-kind match is exhaustive with no `_` arm so a new +callback-bearing `NodeKind` cannot silently drop its root. diff --git a/crates/perry-ui-windows-winui/Cargo.toml b/crates/perry-ui-windows-winui/Cargo.toml index 33f01326cd..938e07d88a 100644 --- a/crates/perry-ui-windows-winui/Cargo.toml +++ b/crates/perry-ui-windows-winui/Cargo.toml @@ -4,40 +4,30 @@ version.workspace = true edition.workspace = true license.workspace = true -# Opt-in WinUI 3 (Fluent) Windows backend — issue #4680. Selected via -# `--target windows-winui`. This is the SCAFFOLD: the crate re-exports the -# Win32/GDI backend (`perry-ui-windows`) verbatim so the full `perry_ui_*` -# FFI ABI is satisfied and apps built for `windows-winui` link and run today -# (rendering through Win32). The WinUI/XAML widget mapping then lands -# incrementally in the `winui` module, replacing Win32 widget creation one -# widget at a time, without ever breaking the link surface. +# Opt-in WinUI 3 (Fluent) Windows backend, selected with +# `--target windows-winui`. Core controls render through Windows Reactor; +# platform services and controls without a Fluent mapping reuse Win32. [lints] workspace = true [lib] # Same shape as perry-ui-windows: an rlib for Rust consumers and a staticlib -# the perry driver /WHOLEARCHIVE's into the final executable. The staticlib -# bundles perry-ui-windows's object code (all its #[no_mangle] extern "C" -# symbols), so no symbol is lost by going through this crate. +# the perry driver /WHOLEARCHIVE's into the final executable. crate-type = ["rlib", "staticlib"] [target.'cfg(target_os = "windows")'.dependencies] -# The entire current Windows backend. Re-exported wholesale (see src/lib.rs). -perry-ui-windows = { path = "../perry-ui-windows" } +perry-ui-windows = { path = "../perry-ui-windows", default-features = false } +perry-ffi.workspace = true +perry-runtime = { path = "../perry-runtime", default-features = false, features = ["stdlib"] } +base64.workspace = true +libc.workspace = true +windows = { version = "0.62", features = ["Win32_Foundation"] } +windows-reactor = { path = "../../third_party/windows-winui/windows-reactor" } -# Step 2 (#4680) — Windows App SDK bootstrap — is implemented in -# `src/winui.rs` via *runtime* dynamic loading (LoadLibraryW + GetProcAddress -# on Microsoft.WindowsAppRuntime.Bootstrap.dll), NOT a link dependency. That -# deliberately keeps the single self-contained .exe model: a windows-winui -# binary starts fine on a host without the SDK and falls back to Win32 instead -# of failing to load. So no bootstrap .lib / cargo crate is needed for it. -# -# Future (XAML widget mapping, #4680 step 3+): driving Microsoft.UI.Xaml -# controls through the windows-rs WinRT projections. When that lands, add: -# windows = { version = "0.58", features = [ -# "UI_Xaml", "UI_Xaml_Controls", "UI_Xaml_Hosting", ... ] } -# gated so it is constructed only after bootstrap::initialize() reports Ready. +# The bootstrap probe is dynamically loaded in `src/winui.rs`, allowing a +# missing Windows App SDK runtime to select the existing Win32 implementation. +# Reactor owns the WinUI application lifecycle after that probe succeeds. [features] geisterhand = ["perry-ui-windows/geisterhand"] diff --git a/crates/perry-ui-windows-winui/src/app.rs b/crates/perry-ui-windows-winui/src/app.rs new file mode 100644 index 0000000000..b022c72009 --- /dev/null +++ b/crates/perry-ui-windows-winui/src/app.rs @@ -0,0 +1,330 @@ +//! WinUI application lifecycle adapter. + +use std::cell::RefCell; +use std::time::Duration; + +use windows::Win32::Foundation::HWND; +use windows_reactor::winui::host::PresenterKind; +use windows_reactor::{App, Backdrop, DispatcherTimer, InnerConstraints}; + +use crate::winui::backend::{self, RenderBackend}; + +extern "C" { + fn js_callback_timer_tick() -> i32; + fn js_closure_call0(closure: *const u8) -> f64; + fn js_frame_pump_default() -> i32; + fn js_gc_step_us(budget_us: u64, out: *mut u8) -> u32; + fn js_interval_timer_tick() -> i32; + fn js_nanbox_get_pointer(value: f64) -> i64; +} + +#[derive(Clone, Default)] +struct AppState { + title: String, + width: f64, + height: f64, + root: i64, + min_size: Option<(f64, f64)>, + max_size: Option<(f64, f64)>, + presenter: PresenterKind, +} + +thread_local! { + static APPS: RefCell> = const { RefCell::new(Vec::new()) }; + static ON_ACTIVATE: RefCell> = const { RefCell::new(None) }; + static ON_TERMINATE: RefCell> = const { RefCell::new(None) }; + static PENDING_TIMERS: RefCell> = const { RefCell::new(Vec::new()) }; + static ACTIVE_TIMERS: RefCell> = const { RefCell::new(Vec::new()) }; + static RUNTIME_PUMP_STARTED: RefCell = const { RefCell::new(false) }; +} + +/// Visit the JavaScript closures this module keeps alive across collections. +/// +/// `APPS` is deliberately absent: `AppState` holds a Rust-owned `String`, two +/// `f64` window dimensions, an `i64` WIDGET handle (a 1-based index into +/// `widgets::NODES`, not an address), two optional size pairs and a +/// `PresenterKind` — no JS value, so it is not a GC root. +/// +/// KNOWN RESIDUAL (not fixable by scanning): `start_runtime_pump` drains +/// `PENDING_TIMERS` into `DispatcherTimer` closures that own a COPY of the raw +/// pointer, and `app_run` moves `ON_TERMINATE` into an `on_exit` closure the +/// same way. Those copies live inside boxed Rust closures owned by Windows +/// Reactor, where no scanner can reach or rewrite them, so an evacuating +/// collection would leave them stale. Making the closures re-read a scanned +/// slot (the indirection `perry-ui-macos` gets from its handle-keyed callback +/// maps) is the real fix and is a follow-up, not a relocation. +pub(crate) fn scan_winui_app_gc_roots(visitor: &mut perry_ffi::GcRootVisitor<'_>) { + for slot in [&ON_ACTIVATE, &ON_TERMINATE] { + slot.with(|slot| { + if let Some(callback) = slot.borrow_mut().as_mut() { + if *callback != 0 { + visitor.visit_usize_slot(callback); + } + } + }); + } + PENDING_TIMERS.with(|timers| { + for (_, callback) in timers.borrow_mut().iter_mut() { + if *callback != 0 { + visitor.visit_usize_slot(callback); + } + } + }); +} + +fn is_fluent() -> bool { + backend::active() == RenderBackend::Fluent +} + +fn closure_ptr(value: f64) -> usize { + unsafe { js_nanbox_get_pointer(value) as usize } +} + +fn with_app_mut(handle: i64, f: impl FnOnce(&mut AppState)) { + APPS.with(|apps| { + if let Some(app) = apps.borrow_mut().get_mut(handle.saturating_sub(1) as usize) { + f(app); + } + }); +} + +pub fn app_create(title_ptr: *const u8, width: f64, height: f64) -> i64 { + crate::gc::ensure_registered(); + if !is_fluent() { + return perry_ui_windows::app::app_create(title_ptr, width, height); + } + let title = unsafe { perry_ffi::copy_string_from_raw(title_ptr) }.to_owned(); + APPS.with(|apps| { + let mut apps = apps.borrow_mut(); + apps.push(AppState { + title, + width, + height, + ..AppState::default() + }); + apps.len() as i64 + }) +} + +pub fn app_set_body(app_handle: i64, root_handle: i64) { + if !is_fluent() { + perry_ui_windows::app::app_set_body(app_handle, root_handle); + return; + } + with_app_mut(app_handle, |app| app.root = root_handle); + crate::widgets::set_root(root_handle); +} + +pub fn app_run(app_handle: i64) { + if !is_fluent() { + perry_ui_windows::app::app_run(app_handle); + return; + } + + let Some(state) = APPS.with(|apps| { + apps.borrow() + .get(app_handle.saturating_sub(1) as usize) + .cloned() + }) else { + return; + }; + crate::widgets::set_root(state.root); + + if let Some(callback) = ON_ACTIVATE.with(|slot| *slot.borrow()) { + unsafe { + js_closure_call0(callback as *const u8); + } + } + + let constraints = InnerConstraints { + min_width: state.min_size.map(|v| v.0), + min_height: state.min_size.map(|v| v.1), + max_width: state.max_size.map(|v| v.0), + max_height: state.max_size.map(|v| v.1), + }; + let mut app = App::new() + .title(state.title) + .inner_size(state.width, state.height) + .inner_constraints(constraints) + .presenter(state.presenter) + .backdrop(Backdrop::Mica); + if let Some(callback) = ON_TERMINATE.with(|slot| slot.borrow_mut().take()) { + app = app.on_exit(move || unsafe { + js_closure_call0(callback as *const u8); + }); + } + if let Err(error) = app.render(crate::widgets::render_root) { + eprintln!("[perry-winui] application failed: {error}"); + } +} + +pub fn get_dpi_scale() -> f64 { + if is_fluent() { + 1.0 + } else { + perry_ui_windows::app::get_dpi_scale() + } +} + +pub fn request_layout() { + if is_fluent() { + crate::widgets::request_render(); + } else { + perry_ui_windows::app::request_layout(); + } +} + +pub fn app_set_size(app_handle: i64, width: f64, height: f64) { + if !is_fluent() { + perry_ui_windows::app::app_set_size(app_handle, width, height); + return; + } + with_app_mut(app_handle, |app| { + app.width = width; + app.height = height; + }); +} + +pub fn set_min_size(app_handle: i64, width: f64, height: f64) { + if !is_fluent() { + perry_ui_windows::app::set_min_size(app_handle, width, height); + return; + } + with_app_mut(app_handle, |app| app.min_size = Some((width, height))); +} + +pub fn set_max_size(app_handle: i64, width: f64, height: f64) { + if !is_fluent() { + perry_ui_windows::app::set_max_size(app_handle, width, height); + return; + } + with_app_mut(app_handle, |app| app.max_size = Some((width, height))); +} + +pub fn set_window_state(app_handle: i64, value_ptr: *const u8) { + if !is_fluent() { + perry_ui_windows::app::set_window_state(app_handle, value_ptr); + return; + } + let value = unsafe { perry_ffi::copy_string_from_raw(value_ptr) }; + let presenter = if value.eq_ignore_ascii_case("fullscreen") { + PresenterKind::FullScreen + } else { + PresenterKind::Default + }; + with_app_mut(app_handle, |app| app.presenter = presenter); +} + +pub fn set_timer(interval_ms: f64, callback: f64) { + if !is_fluent() { + perry_ui_windows::app::set_timer(interval_ms, callback); + return; + } + PENDING_TIMERS.with(|timers| { + timers.borrow_mut().push(( + Duration::from_secs_f64((interval_ms.max(1.0)) / 1000.0), + closure_ptr(callback), + )); + }); +} + +pub(crate) fn start_runtime_pump() { + if !is_fluent() { + return; + } + let already_started = RUNTIME_PUMP_STARTED.with(|started| { + let old = *started.borrow(); + *started.borrow_mut() = true; + old + }); + if already_started { + return; + } + + ACTIVE_TIMERS.with(|active| { + let mut active = active.borrow_mut(); + if let Ok(timer) = DispatcherTimer::new(Duration::from_millis(16), || unsafe { + js_callback_timer_tick(); + js_interval_timer_tick(); + js_frame_pump_default(); + js_gc_step_us(750, std::ptr::null_mut()); + }) { + active.push(timer); + } + PENDING_TIMERS.with(|pending| { + for (interval, callback) in pending.borrow_mut().drain(..) { + if let Ok(timer) = DispatcherTimer::new(interval, move || unsafe { + js_closure_call0(callback as *const u8); + }) { + active.push(timer); + } + } + }); + }); +} + +pub fn on_activate(callback: f64) { + if is_fluent() { + ON_ACTIVATE.with(|slot| *slot.borrow_mut() = Some(closure_ptr(callback))); + } else { + perry_ui_windows::app::on_activate(callback); + } +} + +pub fn on_terminate(callback: f64) { + if is_fluent() { + ON_TERMINATE.with(|slot| *slot.borrow_mut() = Some(closure_ptr(callback))); + } else { + perry_ui_windows::app::on_terminate(callback); + } +} + +pub fn get_main_hwnd() -> Option { + if is_fluent() { + None + } else { + perry_ui_windows::app::get_main_hwnd() + } +} + +pub fn add_keyboard_shortcut(key_ptr: *const u8, modifiers: f64, callback: f64) { + perry_ui_windows::app::add_keyboard_shortcut(key_ptr, modifiers, callback); +} + +pub fn register_global_hotkey(key_ptr: *const u8, modifiers: f64, callback: f64) { + perry_ui_windows::app::register_global_hotkey(key_ptr, modifiers, callback); +} + +pub fn get_app_icon(path_ptr: *const u8) -> i64 { + perry_ui_windows::app::get_app_icon(path_ptr) +} + +pub fn app_set_frameless(app_handle: i64, value: f64) { + if !is_fluent() { + perry_ui_windows::app::app_set_frameless(app_handle, value); + } +} + +pub fn app_set_level(app_handle: i64, value_ptr: *const u8) { + if !is_fluent() { + perry_ui_windows::app::app_set_level(app_handle, value_ptr); + } +} + +pub fn app_set_transparent(app_handle: i64, value: f64) { + if !is_fluent() { + perry_ui_windows::app::app_set_transparent(app_handle, value); + } +} + +pub fn app_set_vibrancy(app_handle: i64, value_ptr: *const u8) { + if !is_fluent() { + perry_ui_windows::app::app_set_vibrancy(app_handle, value_ptr); + } +} + +pub fn app_set_activation_policy(app_handle: i64, value_ptr: *const u8) { + if !is_fluent() { + perry_ui_windows::app::app_set_activation_policy(app_handle, value_ptr); + } +} diff --git a/crates/perry-ui-windows-winui/src/gc.rs b/crates/perry-ui-windows-winui/src/gc.rs new file mode 100644 index 0000000000..19899f2c9a --- /dev/null +++ b/crates/perry-ui-windows-winui/src/gc.rs @@ -0,0 +1,61 @@ +//! GC root custody for the WinUI 3 (Fluent) backend. +//! +//! Every persistent JavaScript callback this crate keeps is stored as a RAW +//! CLOSURE POINTER (`js_nanbox_get_pointer(value) as usize`), so each stored +//! slot is a GC root. An unregistered holder is not an intermittent bug: it +//! goes bad at collection #0 and stays bad, surfacing later as +//! `TypeError: value is not a function` (see +//! `docs/src/internals/gc-rooting-invariant.md`). Same shape and same fix as +//! the sibling backends (`perry-ui-macos/src/gc.rs`, `perry-ui-ios/src/gc.rs`, +//! #8713). +//! +//! Registration also chains `perry-ui-windows`: on the Fluent path this crate +//! shadows `app_create`, so that crate's own `ensure_gc_scanner_registered()` +//! call never runs even though its Win32 tables (menu/tray/toolbar/window/ +//! drag_drop/media_playback/pointer/widgets) are still live behind the +//! re-exports in `lib.rs`. + +use std::cell::Cell; + +use perry_ffi::{gc_register_mutable_root_scanner_named, GcRootVisitor}; + +thread_local! { + /// Per-thread, NOT a process-global `Once`. + /// + /// `gc_register_mutable_root_scanner_named` installs its trampoline + /// through a thread-local registry + /// (`MUTABLE_ROOT_SCANNER_TRAMPOLINES_REGISTERED`, perry-ffi/src/handle.rs), + /// so a process-global latch would let the first thread consume it and + /// leave every later heap running without this scanner — the #8530 shape + /// that `scripts/check_gc_scanner_latches.py` exists to reject. The + /// registration itself is already idempotent per thread; this latch only + /// keeps the common path off the registry mutex. + static GC_REGISTERED: Cell = const { Cell::new(false) }; +} + +/// Register this crate's mutable root scanner once per thread. +/// +/// Called from the two funnels through which a JavaScript callback can first +/// reach this backend: `app::app_create` and `widgets::register`. +pub(crate) fn ensure_registered() { + // NOTE: `perry-ui-windows` still latches on a process-global `Once`, as do + // the other seven UI backends from #8713. Chaining it here is strictly + // better than the status quo (on the Fluent path nothing registered it at + // all); converting that family to per-thread latches is a separate change. + perry_ui_windows::gc::ensure_gc_scanner_registered(); + GC_REGISTERED.with(|registered| { + if registered.replace(true) { + return; + } + gc_register_mutable_root_scanner_named("perry-ui-windows-winui", scan_roots); + }); +} + +fn scan_roots(visitor: &mut GcRootVisitor<'_>) { + crate::app::scan_winui_app_gc_roots(visitor); + // `state.rs` is compiled INTO this crate via `#[path]`, so this crate has + // its own instance of `STATES` / `FOR_EACH_BINDINGS` / `ON_CHANGE_BINDINGS` + // that `perry-ui-windows`' scanner cannot reach. + crate::state::scan_windows_state_gc_roots(visitor); + crate::widgets::scan_winui_widgets_gc_roots(visitor); +} diff --git a/crates/perry-ui-windows-winui/src/lib.rs b/crates/perry-ui-windows-winui/src/lib.rs index b165c45a11..3d1bb9aeb5 100644 --- a/crates/perry-ui-windows-winui/src/lib.rs +++ b/crates/perry-ui-windows-winui/src/lib.rs @@ -1,60 +1,36 @@ -//! Opt-in WinUI 3 (Fluent) Windows backend — issue #4680. +//! Opt-in WinUI 3 / Fluent backend for Perry's Windows target. //! -//! Selected via `--target windows-winui`. Motivation (discussion #3486): the -//! default Win32/GDI chrome looks dated; WinUI 3 / Fluent brings rounded -//! corners, Mica/Acrylic materials, smooth animation, and crisp auto-DPI — -//! the closest Windows analog to Perry's iOS/Android look-and-feel. -//! -//! # Why a separate target (not the default) -//! -//! WinUI 3 requires the **Windows App SDK runtime** plus an MSIX/bootstrapper -//! packaging story, which conflicts with Perry's single-native-`.exe` model. -//! So Win32 (`perry-ui-windows`, `--target windows`) stays the default and the -//! Fluent backend is opt-in, mirroring how Perry ships multiple Apple targets. -//! -//! # Scaffold status (this crate today) -//! -//! This crate currently **re-exports `perry-ui-windows` verbatim**. That makes -//! `--target windows-winui` a real, selectable target immediately: apps build, -//! link, and run — rendering through the Win32 backend for now. Building the -//! staticlib bundles every `#[no_mangle] extern "C"` `perry_ui_*` symbol from -//! `perry-ui-windows`, so the FFI surface the perry driver links against is -//! identical to the `windows` target. Nothing regresses, and there is a stable -//! place to grow the real Fluent backend. -//! -//! # The incremental plan (#4680 work breakdown) -//! -//! The XAML widget mapping lands one widget at a time in [`winui`], each -//! replacing the corresponding Win32 widget creation with a -//! `Microsoft.UI.Xaml` control driven through the windows-rs WinRT -//! projections, until the whole ~40-widget set is Fluent. Steps, in order: -//! -//! 1. WinAppSDK + `Microsoft.UI.Xaml` projections wired in (`Cargo.toml`). -//! 2. Bootstrapper / runtime acquisition ([`winui::bootstrap`]) — **done**: -//! [`winui::bootstrap::initialize`] dynamically loads the Windows App SDK -//! bootstrapper and reports whether the WinUI path is usable, falling back -//! to Win32 (this re-export) when the runtime is absent. -//! 3. Widget mapping layer (perry-ui widget set → XAML controls). The -//! *dispatch seam* is in place ([`winui::backend::active`]): a CRT -//! static initializer probes the SDK at process start and resolves a -//! [`winui::backend::RenderBackend`] (`Fluent` when the runtime is ready, -//! else `Win32`). Each XAML widget lands behind this check; today it always -//! resolves to `Win32` until the first control is wired in. -//! 4. Window chrome: Mica/Acrylic backdrop, Fluent title bar, light/dark/system. -//! 5. Packaging: MSIX or unpackaged WinAppSDK bootstrap; document the runtime. -//! 6. `apply_style` (geisterhand) dispatcher parity with the other backends. -//! -//! Tracks discussion #3486. Near-term Win32 polish on the default target lives -//! in #4681. +//! `--target windows-winui` keeps Perry's stable `perry_ui_*` C ABI but +//! renders the core widget tree with real `Microsoft.UI.Xaml` controls through +//! Microsoft Windows Reactor. `--target windows` continues to compile the +//! unchanged Win32/GDI exports from `perry-ui-windows`. #![cfg(target_os = "windows")] -// Re-export the entire Win32 backend. For Rust consumers this exposes the same -// module API as `perry-ui-windows`; for the C ABI it is a no-op (the -// `#[no_mangle]` entry points are emitted when `perry-ui-windows` is compiled -// and bundled into this crate's staticlib regardless of Rust-level re-export), -// but it documents that this crate IS the Win32 surface until XAML supersedes -// it widget-by-widget. -pub use perry_ui_windows::*; - +pub mod app; +mod gc; +pub mod pointer; +pub mod widgets; pub mod winui; + +// The state implementation is backend-neutral apart from calls through +// `crate::widgets`; compiling it here makes its bindings update the Fluent +// model instead of the Win32 HWND registry. +#[path = "../../perry-ui-windows/src/state.rs"] +pub mod state; + +// Platform services that do not render widgets remain shared with Win32. +pub use perry_ui_windows::{ + audio, audio_playback, clipboard, deeplinks_stub, dialog, dpi_compat, drag_drop, dwm, + file_dialog, folder_dialog, issue_552_stub, keyboard, keychain, layout, media_playback, menu, + network_stub, screenshot, sheet, system, theme, toolbar, tray, window, +}; + +#[cfg(feature = "geisterhand")] +pub use perry_ui_windows::geisterhand_style; + +// Compile the proven ABI wrappers against this crate's `app`, `state`, and +// `widgets` modules. `perry-ui-windows` disables its own `ffi-exports` feature +// in this dependency graph, so these are the archive's only unmangled exports. +#[path = "../../perry-ui-windows/src/ffi/mod.rs"] +pub mod ffi; diff --git a/crates/perry-ui-windows-winui/src/pointer.rs b/crates/perry-ui-windows-winui/src/pointer.rs new file mode 100644 index 0000000000..cb74a9fd0e --- /dev/null +++ b/crates/perry-ui-windows-winui/src/pointer.rs @@ -0,0 +1,27 @@ +//! Generic pointer callback dispatch for Fluent widgets. + +use crate::winui::backend::{self, RenderBackend}; + +pub fn set_on_click(handle: i64, callback: f64) { + if backend::active() == RenderBackend::Fluent { + crate::widgets::set_on_click(handle, callback); + } else { + perry_ui_windows::pointer::set_on_click(handle, callback); + } +} + +pub fn set_on_mouse_down(handle: i64, callback: f64) { + perry_ui_windows::pointer::set_on_mouse_down(handle, callback); +} + +pub fn set_on_mouse_up(handle: i64, callback: f64) { + perry_ui_windows::pointer::set_on_mouse_up(handle, callback); +} + +pub fn set_on_mouse_move(handle: i64, callback: f64) { + perry_ui_windows::pointer::set_on_mouse_move(handle, callback); +} + +pub fn set_on_hover(handle: i64, callback: f64) { + perry_ui_windows::pointer::set_on_hover(handle, callback); +} diff --git a/crates/perry-ui-windows-winui/src/widgets.rs b/crates/perry-ui-windows-winui/src/widgets.rs new file mode 100644 index 0000000000..b075ee69b5 --- /dev/null +++ b/crates/perry-ui-windows-winui/src/widgets.rs @@ -0,0 +1,1407 @@ +//! Perry widget-handle model to Windows Reactor element adapter. + +use std::cell::{Cell, RefCell}; + +use windows::Win32::Foundation::HWND; +use windows_reactor::{ + border, button as fluent_button, grid, hstack as fluent_hstack, text_block, + vstack as fluent_vstack, Border, Brush, ButtonStyle, Callback, Color, Element, ElementExt, + HorizontalAlignment, Modifiers, PasswordBox, ProgressBar, RenderCx, ScrollViewer, SetState, + Slider, TextBlock, TextBox, Thickness, ToggleSwitch, VerticalAlignment, +}; + +use crate::winui::backend::{self, RenderBackend}; + +pub use perry_ui_windows::widgets::{ + ad_banner, attributed_text, bloomview, bottom_nav, calendar, canvas, chart, combobox, + command_palette, date_picker, image, image_gallery, map_view, navstack, pdf_view, picker, + qrcode, rich_text, rich_tooltip, table, textarea, toast, tree_view, webview, WidgetKind, +}; +#[path = "../../perry-ui-windows/src/widgets/text_registry.rs"] +pub mod text_registry; + +extern "C" { + fn js_closure_call0(closure: *const u8) -> f64; + fn js_closure_call1(closure: *const u8, arg: f64) -> f64; + fn js_nanbox_get_pointer(value: f64) -> i64; + fn js_nanbox_string(ptr: i64) -> f64; + fn js_string_from_bytes(data: *const u8, len: u32) -> *mut u8; +} + +#[derive(Clone, Default)] +struct Common { + children: Vec, + hidden: bool, + enabled: bool, + width: Option, + height: Option, + match_parent_width: bool, + match_parent_height: bool, + fills_remaining: bool, + insets: (f64, f64, f64, f64), + opacity: Option, + background: Option, + foreground: Option, + border_color: Option, + border_width: Option, + corner_radius: Option, + tooltip: Option, + on_click: usize, + distribution: i64, + alignment: i64, + detaches_hidden: bool, +} + +#[derive(Clone)] +enum NodeKind { + Text { + value: String, + font_size: Option, + font_weight: Option, + font_family: Option, + selectable: bool, + max_lines: Option, + truncation: bool, + }, + Button { + label: String, + callback: usize, + bordered: bool, + }, + VStack { + spacing: f64, + }, + HStack { + spacing: f64, + }, + ZStack, + Spacer, + Divider, + TextField { + value: String, + placeholder: String, + callback: usize, + borderless: bool, + font_size: Option, + }, + SecureField { + value: String, + placeholder: String, + callback: usize, + }, + Toggle { + label: String, + on: bool, + callback: usize, + }, + Slider { + min: f64, + max: f64, + value: f64, + callback: usize, + }, + ScrollView { + offset: f64, + }, + Form, + Section { + title: String, + }, + LazyVStack { + spacing: f64, + }, + Progress { + value: f64, + }, +} + +#[derive(Clone)] +struct Node { + kind: NodeKind, + common: Common, +} + +impl Node { + fn new(kind: NodeKind) -> Self { + Self { + kind, + common: Common { + enabled: true, + ..Common::default() + }, + } + } +} + +thread_local! { + static NODES: RefCell> = const { RefCell::new(Vec::new()) }; + static ROOT: Cell = const { Cell::new(0) }; + static RENDER_EPOCH: Cell = const { Cell::new(0) }; + static RENDER_SETTER: RefCell>> = const { RefCell::new(None) }; +} + +/// Visit the JavaScript closures the widget tree keeps alive across +/// collections. +/// +/// Every callback here is a RAW CLOSURE POINTER (`callback_ptr` unboxes it via +/// `js_nanbox_get_pointer`), so each stored slot is a GC root that an +/// evacuating collection must rewrite. +/// +/// KNOWN RESIDUAL (not fixable by scanning): `render_handle` works on a CLONE +/// of the node and captures the unboxed pointer by value into the `move` +/// closures it hands to Windows Reactor (`fluent_button(..).on_click(move || +/// invoke0(selected))`, `apply_common`'s `on_tapped`, and the per-widget +/// handlers). Those captured copies live inside boxed Rust closures owned by +/// the element tree, which no scanner can reach or rewrite. Re-reading the +/// scanned `NODES` slot at invoke time — the indirection `perry-ui-macos` gets +/// from its handle-keyed callback maps — is the real fix and is a follow-up. +pub(crate) fn scan_winui_widgets_gc_roots(visitor: &mut perry_ffi::GcRootVisitor<'_>) { + NODES.with(|nodes| { + for node in nodes.borrow_mut().iter_mut() { + // The generic `.onClick` handler every node kind can carry. + if node.common.on_click != 0 { + visitor.visit_usize_slot(&mut node.common.on_click); + } + // ...plus the per-kind handler. This match is deliberately + // exhaustive with no `_` arm: a new `NodeKind` that stores a + // closure has to classify itself here or fail to compile. A + // catch-all would silently drop the new root (CLAUDE.md, + // "Closure Captures"). + let callback = match &mut node.kind { + NodeKind::Button { callback, .. } + | NodeKind::TextField { callback, .. } + | NodeKind::SecureField { callback, .. } + | NodeKind::Toggle { callback, .. } + | NodeKind::Slider { callback, .. } => callback, + NodeKind::Text { .. } + | NodeKind::VStack { .. } + | NodeKind::HStack { .. } + | NodeKind::ZStack + | NodeKind::Spacer + | NodeKind::Divider + | NodeKind::ScrollView { .. } + | NodeKind::Form + | NodeKind::Section { .. } + | NodeKind::LazyVStack { .. } + | NodeKind::Progress { .. } => continue, + }; + if *callback != 0 { + visitor.visit_usize_slot(callback); + } + } + }); +} + +fn is_fluent() -> bool { + backend::active() == RenderBackend::Fluent +} + +fn callback_ptr(value: f64) -> usize { + unsafe { js_nanbox_get_pointer(value) as usize } +} + +fn read_string(ptr: *const u8) -> String { + unsafe { perry_ffi::copy_string_from_raw(ptr) }.to_owned() +} + +fn invoke0(callback: usize) { + if callback != 0 { + unsafe { + js_closure_call0(callback as *const u8); + } + } +} + +fn invoke1(callback: usize, value: f64) { + if callback != 0 { + unsafe { + js_closure_call1(callback as *const u8, value); + } + } +} + +fn invoke_string(callback: usize, value: &str) { + if callback == 0 { + return; + } + unsafe { + let string = js_string_from_bytes(value.as_ptr(), value.len() as u32); + let boxed = js_nanbox_string(string as i64); + js_closure_call1(callback as *const u8, boxed); + } +} + +fn color(r: f64, g: f64, b: f64, a: f64) -> Color { + let channel = |v: f64| (v.clamp(0.0, 1.0) * 255.0).round() as u8; + Color { + a: channel(a), + r: channel(r), + g: channel(g), + b: channel(b), + } +} + +fn register(kind: NodeKind) -> i64 { + crate::gc::ensure_registered(); + NODES.with(|nodes| { + let mut nodes = nodes.borrow_mut(); + nodes.push(Node::new(kind)); + nodes.len() as i64 + }) +} + +fn with_node_mut(handle: i64, update: impl FnOnce(&mut Node)) { + let changed = NODES.with(|nodes| { + let mut nodes = nodes.borrow_mut(); + let Some(node) = nodes.get_mut(handle.saturating_sub(1) as usize) else { + return false; + }; + update(node); + true + }); + if changed { + request_render(); + } +} + +fn node(handle: i64) -> Option { + NODES.with(|nodes| { + nodes + .borrow() + .get(handle.saturating_sub(1) as usize) + .cloned() + }) +} + +pub(crate) fn set_root(handle: i64) { + ROOT.with(|root| root.set(handle)); +} + +pub fn request_render() { + if !is_fluent() { + return; + } + let epoch = RENDER_EPOCH.with(|cell| { + let next = cell.get().wrapping_add(1); + cell.set(next); + next + }); + RENDER_SETTER.with(|slot| { + if let Some(setter) = slot.borrow().as_ref() { + setter.call(epoch); + } + }); +} + +pub(crate) fn render_root(cx: &mut RenderCx) -> Element { + let (epoch, setter) = cx.use_state(RENDER_EPOCH.with(Cell::get)); + let _ = epoch; + RENDER_SETTER.with(|slot| *slot.borrow_mut() = Some(setter)); + crate::app::start_runtime_pump(); + let root = ROOT.with(Cell::get); + let content = render_handle(root); + border(content) + .padding(Thickness::uniform(20.0)) + .horizontal_alignment(HorizontalAlignment::Stretch) + .vertical_alignment(VerticalAlignment::Stretch) + .into() +} + +fn apply_common(modifiers: &mut Modifiers, common: &Common) { + modifiers.width = common.width; + modifiers.height = common.height; + modifiers.opacity = common.opacity; + modifiers.background = common.background.map(Brush::Solid); + modifiers.foreground = common.foreground.map(Brush::Solid); + if common.match_parent_width || common.fills_remaining { + modifiers.horizontal_alignment = Some(HorizontalAlignment::Stretch); + } + if common.match_parent_height || common.fills_remaining { + modifiers.vertical_alignment = Some(VerticalAlignment::Stretch); + } + let (top, left, bottom, right) = common.insets; + if top != 0.0 || left != 0.0 || bottom != 0.0 || right != 0.0 { + modifiers.padding = Some(Thickness { + left, + top, + right, + bottom, + }); + } + if common.on_click != 0 { + let callback = common.on_click; + modifiers + .pointer_handlers + .get_or_insert_with(Default::default) + .on_tapped = Some(Callback::new(move |()| invoke0(callback))); + } +} + +fn render_children(handles: &[i64], detach_hidden: bool) -> Vec { + handles + .iter() + .filter_map(|handle| { + let n = node(*handle)?; + if detach_hidden && n.common.hidden { + None + } else { + Some(render_handle(*handle)) + } + }) + .collect() +} + +fn render_handle(handle: i64) -> Element { + let Some(node) = node(handle) else { + return Element::Empty; + }; + if node.common.hidden { + return Element::Empty; + } + let key = format!("perry-{handle}"); + let mut element: Element = match &node.kind { + NodeKind::Text { + value, + font_size, + font_weight, + font_family, + selectable: _, + max_lines: _, + truncation: _, + } => { + let mut view = TextBlock::new(value.clone()); + view.font_size = *font_size; + view.font_weight = *font_weight; + view.modifiers.font_family = font_family.clone(); + apply_common(&mut view.modifiers, &node.common); + view.into() + } + NodeKind::Button { + label, + callback, + bordered, + } => { + let selected = if node.common.on_click != 0 { + node.common.on_click + } else { + *callback + }; + let mut view = fluent_button(label.clone()).on_click(move || invoke0(selected)); + view.style = if *bordered { + ButtonStyle::Default + } else { + ButtonStyle::Subtle + }; + view.is_enabled = node.common.enabled; + apply_common(&mut view.modifiers, &node.common); + // `Button::on_click` already owns the generic callback. + view.modifiers.pointer_handlers = None; + view.into() + } + NodeKind::VStack { spacing } | NodeKind::LazyVStack { spacing } => { + let children = render_children(&node.common.children, node.common.detaches_hidden); + let mut view = fluent_vstack(children).spacing(*spacing); + apply_common(&mut view.modifiers, &node.common); + view.into() + } + NodeKind::HStack { spacing } => { + let children = render_children(&node.common.children, node.common.detaches_hidden); + let mut view = fluent_hstack(children).spacing(*spacing); + apply_common(&mut view.modifiers, &node.common); + view.into() + } + NodeKind::ZStack => { + let children = render_children(&node.common.children, node.common.detaches_hidden); + let mut view = grid(children); + apply_common(&mut view.modifiers, &node.common); + view.into() + } + NodeKind::Spacer => { + let mut view = Border::default(); + view.modifiers.min_width = Some(8.0); + view.modifiers.min_height = Some(8.0); + apply_common(&mut view.modifiers, &node.common); + view.into() + } + NodeKind::Divider => { + let mut view = Border::default(); + view.modifiers.height = Some(1.0); + view.modifiers.horizontal_alignment = Some(HorizontalAlignment::Stretch); + view.modifiers.background = Some(Brush::Solid(Color::rgb(128, 128, 128))); + apply_common(&mut view.modifiers, &node.common); + view.into() + } + NodeKind::TextField { + value, + placeholder, + callback, + borderless: _, + font_size, + } => { + let cb = *callback; + let mut view = TextBox::new(value.clone()) + .placeholder(placeholder.clone()) + .on_changed(move |value: String| { + set_textfield_value(handle, value.clone()); + invoke_string(cb, &value); + }); + view.is_enabled = node.common.enabled; + view.modifiers.font_size = *font_size; + apply_common(&mut view.modifiers, &node.common); + view.into() + } + NodeKind::SecureField { + value, + placeholder, + callback, + } => { + let cb = *callback; + let mut view = PasswordBox::new() + .value(value.clone()) + .placeholder(placeholder.clone()) + .on_changed(move |value: String| { + set_securefield_value(handle, value.clone()); + invoke_string(cb, &value); + }); + view.is_enabled = node.common.enabled; + apply_common(&mut view.modifiers, &node.common); + view.into() + } + NodeKind::Toggle { + label, + on, + callback, + } => { + let cb = *callback; + let mut view = ToggleSwitch::new(*on) + .header(label.clone()) + .on_changed(move |value| { + set_toggle_value(handle, value); + invoke1(cb, if value { 1.0 } else { 0.0 }); + }); + view.is_enabled = node.common.enabled; + apply_common(&mut view.modifiers, &node.common); + view.into() + } + NodeKind::Slider { + min, + max, + value, + callback, + } => { + let cb = *callback; + let mut view = Slider::new(*value) + .range(*min, *max) + .on_changed(move |value| { + set_slider_value(handle, value); + invoke1(cb, value); + }); + view.is_enabled = node.common.enabled; + apply_common(&mut view.modifiers, &node.common); + view.into() + } + NodeKind::ScrollView { .. } => { + let child = node + .common + .children + .first() + .copied() + .map(render_handle) + .unwrap_or(Element::Empty); + let mut view = ScrollViewer::new(child); + apply_common(&mut view.modifiers, &node.common); + view.into() + } + NodeKind::Form => { + let children = render_children(&node.common.children, node.common.detaches_hidden); + let mut view = fluent_vstack(children).spacing(12.0); + apply_common(&mut view.modifiers, &node.common); + view.into() + } + NodeKind::Section { title } => { + let mut children = vec![text_block(title.clone()).semibold().into()]; + children.extend(render_children( + &node.common.children, + node.common.detaches_hidden, + )); + let mut view = fluent_vstack(children).spacing(8.0); + apply_common(&mut view.modifiers, &node.common); + view.into() + } + NodeKind::Progress { value } => { + let mut view = if *value < 0.0 { + ProgressBar::indeterminate() + } else { + ProgressBar::new(value.clamp(0.0, 1.0)).range(0.0, 1.0) + }; + apply_common(&mut view.modifiers, &node.common); + view.into() + } + }; + + // Resource-backed border properties are available on the Border variants + // used for layout primitives. Other controls retain their native Fluent + // stroke and corner resources. + if let Element::Border(view) = &mut element { + view.corner_radius = node.common.corner_radius; + view.border_thickness = node.common.border_width.map(Thickness::uniform); + view.border_brush = node.common.border_color.map(Into::into); + } + element.with_key(key) +} + +pub fn add_child(parent: i64, child: i64) { + if !is_fluent() { + perry_ui_windows::widgets::add_child(parent, child); + return; + } + with_node_mut(parent, |node| node.common.children.push(child)); +} + +pub fn add_child_at(parent: i64, child: i64, index: i64) { + if !is_fluent() { + perry_ui_windows::widgets::add_child_at(parent, child, index); + return; + } + with_node_mut(parent, |node| { + let index = index.max(0) as usize; + let index = index.min(node.common.children.len()); + node.common.children.insert(index, child); + }); +} + +pub fn remove_child(parent: i64, child: i64) { + if !is_fluent() { + perry_ui_windows::widgets::remove_child(parent, child); + return; + } + with_node_mut(parent, |node| node.common.children.retain(|h| *h != child)); +} + +pub fn clear_children(handle: i64) { + if !is_fluent() { + perry_ui_windows::widgets::clear_children(handle); + return; + } + with_node_mut(handle, |node| node.common.children.clear()); +} + +pub fn set_fixed_width(handle: i64, width: i32) { + if !is_fluent() { + perry_ui_windows::widgets::set_fixed_width(handle, width); + return; + } + with_node_mut(handle, |node| node.common.width = Some(width as f64)); +} + +pub fn set_fixed_height(handle: i64, height: i32) { + if !is_fluent() { + perry_ui_windows::widgets::set_fixed_height(handle, height); + return; + } + with_node_mut(handle, |node| node.common.height = Some(height as f64)); +} + +pub fn set_match_parent_width(handle: i64, value: bool) { + if !is_fluent() { + perry_ui_windows::widgets::set_match_parent_width(handle, value); + return; + } + with_node_mut(handle, |node| node.common.match_parent_width = value); +} + +pub fn set_match_parent_height(handle: i64, value: bool) { + if !is_fluent() { + perry_ui_windows::widgets::set_match_parent_height(handle, value); + return; + } + with_node_mut(handle, |node| node.common.match_parent_height = value); +} + +pub fn set_fills_remaining(handle: i64, value: bool) { + if !is_fluent() { + perry_ui_windows::widgets::set_fills_remaining(handle, value); + return; + } + with_node_mut(handle, |node| node.common.fills_remaining = value); +} + +pub fn set_hugging_priority(handle: i64, priority: f64) { + if !is_fluent() { + perry_ui_windows::widgets::set_hugging_priority(handle, priority); + return; + } + if priority <= 250.0 { + set_fills_remaining(handle, true); + } +} + +pub fn set_hidden(handle: i64, value: bool) { + if !is_fluent() { + perry_ui_windows::widgets::set_hidden(handle, value); + return; + } + with_node_mut(handle, |node| node.common.hidden = value); +} + +pub fn set_enabled(handle: i64, value: bool) { + if !is_fluent() { + perry_ui_windows::widgets::set_enabled(handle, value); + return; + } + with_node_mut(handle, |node| node.common.enabled = value); +} + +pub fn set_detaches_hidden(handle: i64, value: bool) { + if !is_fluent() { + perry_ui_windows::widgets::set_detaches_hidden(handle, value); + return; + } + with_node_mut(handle, |node| node.common.detaches_hidden = value); +} + +pub fn set_distribution(handle: i64, value: i64) { + if !is_fluent() { + perry_ui_windows::widgets::set_distribution(handle, value); + return; + } + with_node_mut(handle, |node| node.common.distribution = value); +} + +pub fn set_alignment(handle: i64, value: i64) { + if !is_fluent() { + perry_ui_windows::widgets::set_alignment(handle, value); + return; + } + with_node_mut(handle, |node| node.common.alignment = value); +} + +pub fn set_insets(handle: i64, top: f64, left: f64, bottom: f64, right: f64) { + if !is_fluent() { + perry_ui_windows::widgets::set_insets(handle, top, left, bottom, right); + return; + } + with_node_mut(handle, |node| { + node.common.insets = (top, left, bottom, right) + }); +} + +pub fn set_background_color(handle: i64, r: f64, g: f64, b: f64, a: f64) { + if !is_fluent() { + perry_ui_windows::widgets::set_background_color(handle, r, g, b, a); + return; + } + with_node_mut(handle, |node| { + node.common.background = Some(color(r, g, b, a)) + }); +} + +#[allow(clippy::too_many_arguments)] +pub fn set_background_gradient( + handle: i64, + r1: f64, + g1: f64, + b1: f64, + a1: f64, + _r2: f64, + _g2: f64, + _b2: f64, + _a2: f64, + _direction: f64, +) { + if !is_fluent() { + perry_ui_windows::widgets::set_background_gradient( + handle, r1, g1, b1, a1, _r2, _g2, _b2, _a2, _direction, + ); + return; + } + // Reactor's WinUI backend supports Composition animation; Perry's public + // two-stop gradient remains represented by its first color until the + // cross-platform style ABI exposes gradient stops as a typed collection. + set_background_color(handle, r1, g1, b1, a1); +} + +pub fn set_opacity(handle: i64, value: f64) { + if !is_fluent() { + perry_ui_windows::widgets::set_opacity(handle, value); + return; + } + with_node_mut(handle, |node| { + node.common.opacity = Some(value.clamp(0.0, 1.0)) + }); +} + +pub fn set_corner_radius(handle: i64, value: f64) { + if !is_fluent() { + perry_ui_windows::widgets::set_corner_radius(handle, value); + return; + } + with_node_mut(handle, |node| { + node.common.corner_radius = Some(value.max(0.0)) + }); +} + +pub fn set_border_color(handle: i64, r: f64, g: f64, b: f64, a: f64) { + if !is_fluent() { + perry_ui_windows::widgets::set_border_color(handle, r, g, b, a); + return; + } + with_node_mut(handle, |node| { + node.common.border_color = Some(color(r, g, b, a)) + }); +} + +pub fn set_border_width(handle: i64, value: f64) { + if !is_fluent() { + perry_ui_windows::widgets::set_border_width(handle, value); + return; + } + with_node_mut(handle, |node| { + node.common.border_width = Some(value.max(0.0)) + }); +} + +#[allow(clippy::too_many_arguments)] +pub fn set_shadow( + handle: i64, + r: f64, + g: f64, + b: f64, + a: f64, + blur: f64, + offset_x: f64, + offset_y: f64, +) { + if !is_fluent() { + perry_ui_windows::widgets::set_shadow(handle, r, g, b, a, blur, offset_x, offset_y); + } +} + +pub fn set_tooltip(handle: i64, text_ptr: *const u8) { + if !is_fluent() { + perry_ui_windows::widgets::set_tooltip(handle, text_ptr); + return; + } + let value = read_string(text_ptr); + with_node_mut(handle, |node| node.common.tooltip = Some(value)); +} + +pub fn set_control_size(handle: i64, size: i64) { + if !is_fluent() { + perry_ui_windows::widgets::set_control_size(handle, size); + } +} + +pub fn set_on_click(handle: i64, callback: f64) { + let callback = callback_ptr(callback); + with_node_mut(handle, |node| node.common.on_click = callback); +} + +pub fn set_on_hover(handle: i64, callback: f64) { + if !is_fluent() { + perry_ui_windows::widgets::set_on_hover(handle, callback); + } +} + +pub fn set_on_double_click(handle: i64, callback: f64) { + if !is_fluent() { + perry_ui_windows::widgets::set_on_double_click(handle, callback); + } +} + +pub fn animate_opacity(handle: i64, target: f64, _duration: f64) { + if !is_fluent() { + perry_ui_windows::widgets::animate_opacity(handle, target, _duration); + return; + } + set_opacity(handle, target); +} + +pub fn animate_position(handle: i64, dx: f64, dy: f64, duration: f64) { + if !is_fluent() { + perry_ui_windows::widgets::animate_position(handle, dx, dy, duration); + } +} + +pub fn get_hwnd(handle: i64) -> Option { + if is_fluent() { + None + } else { + perry_ui_windows::widgets::get_hwnd(handle) + } +} + +pub fn register_widget(hwnd: HWND, kind: WidgetKind, control_id: u16) -> i64 { + if is_fluent() { + 0 + } else { + perry_ui_windows::widgets::register_widget(hwnd, kind, control_id) + } +} + +fn set_textfield_value(handle: i64, value: String) { + with_node_mut(handle, |node| { + if let NodeKind::TextField { value: current, .. } = &mut node.kind { + *current = value; + } + }); +} + +fn set_securefield_value(handle: i64, value: String) { + with_node_mut(handle, |node| { + if let NodeKind::SecureField { value: current, .. } = &mut node.kind { + *current = value; + } + }); +} + +fn set_toggle_value(handle: i64, value: bool) { + with_node_mut(handle, |node| { + if let NodeKind::Toggle { on, .. } = &mut node.kind { + *on = value; + } + }); +} + +fn set_slider_value(handle: i64, value: f64) { + with_node_mut(handle, |node| { + if let NodeKind::Slider { value: current, .. } = &mut node.kind { + *current = value; + } + }); +} + +pub mod text { + use super::*; + + pub fn create(text_ptr: *const u8) -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::text::create(text_ptr); + } + register(NodeKind::Text { + value: read_string(text_ptr), + font_size: None, + font_weight: None, + font_family: None, + selectable: false, + max_lines: None, + truncation: false, + }) + } + + pub fn set_string(handle: i64, text_ptr: *const u8) { + set_text_str(handle, &read_string(text_ptr)); + } + + pub fn set_text_str(handle: i64, value: &str) { + if !is_fluent() { + perry_ui_windows::widgets::text::set_text_str(handle, value); + return; + } + with_node_mut(handle, |node| { + if let NodeKind::Text { value: current, .. } = &mut node.kind { + *current = value.to_owned(); + } + }); + } + + pub fn set_color(handle: i64, r: f64, g: f64, b: f64, a: f64) { + if !is_fluent() { + perry_ui_windows::widgets::text::set_color(handle, r, g, b, a); + return; + } + with_node_mut(handle, |node| { + node.common.foreground = Some(color(r, g, b, a)) + }); + } + + pub fn set_font_size(handle: i64, size: f64) { + if !is_fluent() { + perry_ui_windows::widgets::text::set_font_size(handle, size); + return; + } + with_node_mut(handle, |node| { + if let NodeKind::Text { font_size, .. } = &mut node.kind { + *font_size = Some(size); + } + }); + } + + pub fn set_font_weight(handle: i64, _size: f64, weight: f64) { + if !is_fluent() { + perry_ui_windows::widgets::text::set_font_weight(handle, _size, weight); + return; + } + with_node_mut(handle, |node| { + if let NodeKind::Text { font_weight, .. } = &mut node.kind { + *font_weight = Some(weight.clamp(1.0, 1000.0) as u16); + } + }); + } + + pub fn set_selectable(handle: i64, value: bool) { + if !is_fluent() { + perry_ui_windows::widgets::text::set_selectable(handle, value); + return; + } + with_node_mut(handle, |node| { + if let NodeKind::Text { selectable, .. } = &mut node.kind { + *selectable = value; + } + }); + } + + pub fn set_number_of_lines(handle: i64, value: i64) { + if !is_fluent() { + perry_ui_windows::widgets::text::set_number_of_lines(handle, value); + return; + } + with_node_mut(handle, |node| { + if let NodeKind::Text { max_lines, .. } = &mut node.kind { + *max_lines = i32::try_from(value).ok(); + } + }); + } + + pub fn set_truncation_mode(handle: i64, _mode: i64) { + if !is_fluent() { + perry_ui_windows::widgets::text::set_truncation_mode(handle, _mode); + return; + } + with_node_mut(handle, |node| { + if let NodeKind::Text { truncation, .. } = &mut node.kind { + *truncation = true; + } + }); + } + + pub fn set_font_family(handle: i64, family_ptr: *const u8) { + if !is_fluent() { + perry_ui_windows::widgets::text::set_font_family(handle, family_ptr); + return; + } + let family = read_string(family_ptr); + with_node_mut(handle, |node| { + if let NodeKind::Text { font_family, .. } = &mut node.kind { + *font_family = Some(family); + } + }); + } + + pub fn set_decoration(handle: i64, decoration: i64) { + if !is_fluent() { + perry_ui_windows::widgets::text::set_decoration(handle, decoration); + } + } + pub fn set_text_alignment(handle: i64, alignment: i64) { + if !is_fluent() { + perry_ui_windows::widgets::text::set_text_alignment(handle, alignment); + } + } +} + +pub mod button { + use super::*; + + pub fn create(label_ptr: *const u8, callback: f64) -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::button::create(label_ptr, callback); + } + register(NodeKind::Button { + label: read_string(label_ptr), + callback: callback_ptr(callback), + bordered: true, + }) + } + + pub fn set_title(handle: i64, title_ptr: *const u8) { + if !is_fluent() { + perry_ui_windows::widgets::button::set_title(handle, title_ptr); + return; + } + let title = read_string(title_ptr); + with_node_mut(handle, |node| { + if let NodeKind::Button { label, .. } = &mut node.kind { + *label = title; + } + }); + } + + pub fn set_bordered(handle: i64, value: bool) { + if !is_fluent() { + perry_ui_windows::widgets::button::set_bordered(handle, value); + return; + } + with_node_mut(handle, |node| { + if let NodeKind::Button { bordered, .. } = &mut node.kind { + *bordered = value; + } + }); + } + + pub fn set_text_color(handle: i64, r: f64, g: f64, b: f64, a: f64) { + if !is_fluent() { + perry_ui_windows::widgets::button::set_text_color(handle, r, g, b, a); + return; + } + with_node_mut(handle, |node| { + node.common.foreground = Some(color(r, g, b, a)) + }); + } + + pub fn set_image(handle: i64, name_ptr: *const u8) { + if !is_fluent() { + perry_ui_windows::widgets::button::set_image(handle, name_ptr); + } + } + pub fn set_image_position(handle: i64, position: i64) { + if !is_fluent() { + perry_ui_windows::widgets::button::set_image_position(handle, position); + } + } +} + +pub mod vstack { + use super::*; + pub fn create(spacing: f64) -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::vstack::create(spacing); + } + register(NodeKind::VStack { spacing }) + } + pub fn create_with_insets(spacing: f64, top: f64, left: f64, bottom: f64, right: f64) -> i64 { + let handle = create(spacing); + set_insets(handle, top, left, bottom, right); + handle + } +} + +pub mod hstack { + use super::*; + pub fn create(spacing: f64) -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::hstack::create(spacing); + } + register(NodeKind::HStack { spacing }) + } + pub fn create_with_insets(spacing: f64, top: f64, left: f64, bottom: f64, right: f64) -> i64 { + let handle = create(spacing); + set_insets(handle, top, left, bottom, right); + handle + } +} + +pub mod zstack { + use super::*; + pub fn create() -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::zstack::create(); + } + register(NodeKind::ZStack) + } +} + +pub mod spacer { + use super::*; + pub fn create() -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::spacer::create(); + } + let handle = register(NodeKind::Spacer); + set_fills_remaining(handle, true); + handle + } +} + +pub mod divider { + use super::*; + pub fn create() -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::divider::create(); + } + register(NodeKind::Divider) + } +} + +pub mod textfield { + use super::*; + + pub fn create(placeholder_ptr: *const u8, callback: f64) -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::textfield::create(placeholder_ptr, callback); + } + register(NodeKind::TextField { + value: String::new(), + placeholder: read_string(placeholder_ptr), + callback: callback_ptr(callback), + borderless: false, + font_size: None, + }) + } + + pub fn set_string_value(handle: i64, value_ptr: *const u8) { + set_string_str(handle, &read_string(value_ptr)); + } + + pub fn set_string_str(handle: i64, value: &str) { + if !is_fluent() { + let bytes = value.as_bytes(); + unsafe { + let ptr = js_string_from_bytes(bytes.as_ptr(), bytes.len() as u32); + perry_ui_windows::widgets::textfield::set_string_value(handle, ptr); + } + return; + } + set_textfield_value(handle, value.to_owned()); + } + + pub fn get_string(handle: i64) -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::textfield::get_string(handle); + } + let value = node(handle) + .and_then(|node| match node.kind { + NodeKind::TextField { value, .. } => Some(value), + _ => None, + }) + .unwrap_or_default(); + unsafe { js_string_from_bytes(value.as_ptr(), value.len() as u32) as i64 } + } + + pub fn set_borderless(handle: i64, value: f64) { + if !is_fluent() { + perry_ui_windows::widgets::textfield::set_borderless(handle, value); + return; + } + with_node_mut(handle, |node| { + if let NodeKind::TextField { borderless, .. } = &mut node.kind { + *borderless = value > 0.5; + } + }); + } + + pub fn set_font_size(handle: i64, value: f64) { + if !is_fluent() { + perry_ui_windows::widgets::textfield::set_font_size(handle, value); + return; + } + with_node_mut(handle, |node| { + if let NodeKind::TextField { font_size, .. } = &mut node.kind { + *font_size = Some(value); + } + }); + } + + pub fn set_background_color(handle: i64, r: f64, g: f64, b: f64, a: f64) { + super::set_background_color(handle, r, g, b, a); + } + pub fn set_text_color(handle: i64, r: f64, g: f64, b: f64, a: f64) { + if !is_fluent() { + perry_ui_windows::widgets::textfield::set_text_color(handle, r, g, b, a); + return; + } + with_node_mut(handle, |node| { + node.common.foreground = Some(color(r, g, b, a)) + }); + } + pub fn focus(handle: i64) { + if !is_fluent() { + perry_ui_windows::widgets::textfield::focus(handle); + } + } +} + +pub mod securefield { + use super::*; + pub fn create(placeholder_ptr: *const u8, callback: f64) -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::securefield::create(placeholder_ptr, callback); + } + register(NodeKind::SecureField { + value: String::new(), + placeholder: read_string(placeholder_ptr), + callback: callback_ptr(callback), + }) + } +} + +pub mod toggle { + use super::*; + pub fn create(label_ptr: *const u8, callback: f64) -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::toggle::create(label_ptr, callback); + } + register(NodeKind::Toggle { + label: read_string(label_ptr), + on: false, + callback: callback_ptr(callback), + }) + } + pub fn set_state(handle: i64, value: i32) { + if !is_fluent() { + perry_ui_windows::widgets::toggle::set_state(handle, value); + return; + } + set_toggle_value(handle, value != 0); + } +} + +pub mod slider { + use super::*; + pub fn create(min: f64, max: f64, value: f64, callback: f64) -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::slider::create(min, max, value, callback); + } + register(NodeKind::Slider { + min, + max, + value, + callback: callback_ptr(callback), + }) + } + pub fn set_value(handle: i64, value: f64) { + if !is_fluent() { + perry_ui_windows::widgets::slider::set_value(handle, value); + return; + } + set_slider_value(handle, value); + } + pub fn get_value(handle: i64) -> Option { + if !is_fluent() { + return perry_ui_windows::widgets::slider::get_value(handle); + } + node(handle).and_then(|node| match node.kind { + NodeKind::Slider { value, .. } => Some(value), + _ => None, + }) + } +} + +pub mod scrollview { + use super::*; + pub fn create() -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::scrollview::create(); + } + register(NodeKind::ScrollView { offset: 0.0 }) + } + pub fn set_child(handle: i64, child: i64) { + if !is_fluent() { + perry_ui_windows::widgets::scrollview::set_child(handle, child); + return; + } + with_node_mut(handle, |node| node.common.children = vec![child]); + } + pub fn set_offset(handle: i64, value: f64) { + if !is_fluent() { + perry_ui_windows::widgets::scrollview::set_offset(handle, value); + return; + } + with_node_mut(handle, |node| { + if let NodeKind::ScrollView { offset } = &mut node.kind { + *offset = value; + } + }); + } + pub fn get_offset(handle: i64) -> f64 { + if !is_fluent() { + return perry_ui_windows::widgets::scrollview::get_offset(handle); + } + node(handle) + .and_then(|node| match node.kind { + NodeKind::ScrollView { offset } => Some(offset), + _ => None, + }) + .unwrap_or(0.0) + } + pub fn set_scroll_end_callback(handle: i64, callback: f64, threshold_px: f64) { + if !is_fluent() { + perry_ui_windows::widgets::scrollview::set_scroll_end_callback( + handle, + callback, + threshold_px, + ); + } + } +} + +pub mod form { + use super::*; + pub fn create() -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::form::create(); + } + register(NodeKind::Form) + } + pub fn section_create(title_ptr: *const u8) -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::form::section_create(title_ptr); + } + register(NodeKind::Section { + title: read_string(title_ptr), + }) + } +} + +pub mod lazyvstack { + use super::*; + pub fn create(_count: f64, _render: f64) -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::lazyvstack::create(_count, _render); + } + register(NodeKind::LazyVStack { spacing: 4.0 }) + } + pub fn update(_handle: i64, _count: i64) { + if !is_fluent() { + perry_ui_windows::widgets::lazyvstack::update(_handle, _count); + } + } +} + +pub mod progressview { + use super::*; + pub fn create() -> i64 { + if !is_fluent() { + return perry_ui_windows::widgets::progressview::create(); + } + register(NodeKind::Progress { value: -1.0 }) + } + pub fn set_value(handle: i64, value: f64) { + if !is_fluent() { + perry_ui_windows::widgets::progressview::set_value(handle, value); + return; + } + with_node_mut(handle, |node| { + if let NodeKind::Progress { value: current } = &mut node.kind { + *current = value; + } + }); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn fluent_model_preserves_tree_order_and_properties() { + let parent = register(NodeKind::VStack { spacing: 12.0 }); + let first = register(NodeKind::Text { + value: "first".into(), + font_size: None, + font_weight: None, + font_family: None, + selectable: false, + max_lines: None, + truncation: false, + }); + let second = register(NodeKind::Divider); + with_node_mut(parent, |node| { + node.common.children.push(first); + node.common.children.push(second); + }); + let node = node(parent).unwrap(); + assert_eq!(node.common.children, vec![first, second]); + assert_eq!(render_handle(parent).kind_name(), "StackPanel"); + } +} diff --git a/crates/perry-ui-windows-winui/src/winui.rs b/crates/perry-ui-windows-winui/src/winui.rs index 47cad102a1..6fd227e4d6 100644 --- a/crates/perry-ui-windows-winui/src/winui.rs +++ b/crates/perry-ui-windows-winui/src/winui.rs @@ -1,10 +1,8 @@ //! WinUI 3 / Fluent backend internals — issue #4680. //! -//! This module is where the Win32 widget creation is progressively replaced by -//! `Microsoft.UI.Xaml` controls. It is empty of real XAML today (scaffold); -//! see the crate-level docs for the incremental plan. Each future widget gets a -//! submodule here that drives the corresponding XAML control and is wired into -//! the dispatch path in place of the `perry-ui-windows` Win32 path. +//! Core controls are mapped to `Microsoft.UI.Xaml` by the adapter in +//! `widgets.rs`; this module owns the runtime bootstrap and stable backend +//! selection used by that adapter. /// Windows App SDK bootstrap (#4680 step 2). /// @@ -13,18 +11,15 @@ /// bootstrapper entry points (`MddBootstrapInitialize2` / /// `MddBootstrapInitialize`) in `Microsoft.WindowsAppRuntime.Bootstrap.dll`. /// -/// # Why dynamic loading (not a link dependency) +/// # Why Perry probes dynamically /// -/// Perry's defining constraint is the single self-contained `.exe`. Linking -/// `Microsoft.WindowsAppRuntime.Bootstrap.lib` would make *every* -/// `windows-winui` binary hard-require the Windows App SDK at load time — the -/// process would fail to start on a machine that doesn't have it, even though -/// the scaffold can fall back to the Win32 backend and run fine. So instead of -/// a link-time import we resolve the bootstrapper at runtime with -/// `LoadLibraryW` + `GetProcAddress`. If the DLL isn't present (no Windows App -/// SDK installed), [`initialize`] reports [`InitStatus::RuntimeMissing`] and -/// the caller falls back to Win32 rather than crashing. This keeps the binary -/// dependency-free; the SDK is consumed only when the host actually has it. +/// Windows Reactor links the bootstrap import library for the Fluent app +/// lifecycle, and Perry deploys the corresponding bootstrap DLL next to each +/// generated executable. Before constructing any XAML objects, Perry resolves +/// that DLL with `LoadLibraryW` + `GetProcAddress` and probes for a compatible +/// Windows App SDK runtime. If the runtime package is unavailable or cannot be +/// initialized, [`initialize`] reports [`InitStatus::RuntimeMissing`] and the +/// caller uses the existing Win32 rendering path. /// /// The result is cached after the first call: the runtime is process-wide and /// initialized at most once, so repeated [`initialize`] calls are cheap and @@ -173,23 +168,11 @@ pub mod bootstrap { /// `MddBootstrapInitializeOptions_None`. const MDD_BOOTSTRAP_OPTIONS_NONE: i32 = 0; - /// Packed `major << 16 | minor` Windows App SDK release the binary was - /// built against. Defaults to 1.6 (the current servicing baseline) and - /// is overridable at runtime with `PERRY_WINAPPSDK_VERSION="major.minor"` - /// so a host with a different SDK can be targeted without a rebuild. + /// Packed `major << 16 | minor` Windows App SDK release the vendored + /// Reactor snapshot was generated against. This must stay in lockstep + /// with `WINDOWSAPPSDK_RELEASE_MAJORMINOR` in Reactor's bindings. fn target_major_minor() -> u32 { - const DEFAULT_MAJOR: u32 = 1; - const DEFAULT_MINOR: u32 = 6; - if let Ok(raw) = std::env::var("PERRY_WINAPPSDK_VERSION") { - if let Some((maj, min)) = raw.split_once('.') { - if let (Ok(maj), Ok(min)) = - (maj.trim().parse::(), min.trim().parse::()) - { - return ((maj as u32) << 16) | (min as u32); - } - } - } - (DEFAULT_MAJOR << 16) | DEFAULT_MINOR + 0x0002_0000 } fn wide_nul(s: &str) -> Vec { @@ -208,10 +191,11 @@ pub mod bootstrap { } let major_minor = target_major_minor(); - // Stable release channel uses an empty version tag; minimum package - // version 0 accepts any installed framework at/above major_minor. + // Stable release channel uses an empty version tag. Keep the + // minimum package version in lockstep with Reactor's + // `WINDOWSAPPSDK_RUNTIME_VERSION_UINT64` (2.0.1.0). let version_tag = wide_nul(""); - let min_version: u64 = 0; + let min_version: u64 = 562_949_953_486_848; // SAFETY: the function pointers come from this freshly-loaded module // and are transmuted to the documented `MddBootstrap.h` signatures. diff --git a/crates/perry-ui-windows/Cargo.toml b/crates/perry-ui-windows/Cargo.toml index 1df4c39df2..fac6f9ac8c 100644 --- a/crates/perry-ui-windows/Cargo.toml +++ b/crates/perry-ui-windows/Cargo.toml @@ -113,4 +113,9 @@ windows-xaml = { package = "windows", version = "0.35", features = [ ] } [features] +default = ["ffi-exports"] +# Keep the C ABI in the default Win32 staticlib, while allowing the WinUI +# backend to reuse the implementation modules and provide its own dispatching +# exports without duplicate `perry_ui_*` symbols in the final archive. +ffi-exports = [] geisterhand = [] diff --git a/crates/perry-ui-windows/src/gc.rs b/crates/perry-ui-windows/src/gc.rs index d52ddec9aa..1aae0d2999 100644 --- a/crates/perry-ui-windows/src/gc.rs +++ b/crates/perry-ui-windows/src/gc.rs @@ -2,7 +2,7 @@ use std::sync::Once; static GC_SCANNER_REGISTERED: Once = Once::new(); -pub(crate) fn ensure_gc_scanner_registered() { +pub fn ensure_gc_scanner_registered() { perry_ui::ensure_gc_scanner_registered(); GC_SCANNER_REGISTERED.call_once(|| { perry_ffi::gc_register_mutable_root_scanner_named( diff --git a/crates/perry-ui-windows/src/lib.rs b/crates/perry-ui-windows/src/lib.rs index a3ed0edeea..0d2fcf825d 100644 --- a/crates/perry-ui-windows/src/lib.rs +++ b/crates/perry-ui-windows/src/lib.rs @@ -9,7 +9,12 @@ pub mod dpi_compat; pub mod drag_drop; #[cfg(target_os = "windows")] pub mod dwm; -mod gc; +// `pub` so the opt-in WinUI backend (`perry-ui-windows-winui`) can chain this +// crate's scanner. On the Fluent path that crate shadows `app_create`, so +// `app::app_create` below never runs and would otherwise never register it, +// leaving the Win32 tables this crate still owns (menu/tray/toolbar/window/ +// drag_drop/media_playback/pointer/widgets) unscanned. +pub mod gc; pub mod issue_552_stub; #[cfg(target_os = "windows")] pub mod keyboard; @@ -166,4 +171,5 @@ pub mod geisterhand_style; // FFI exports — split topically into `ffi/*` sub-modules. Each `#[no_mangle] // pub extern "C" fn perry_ui_<...>` symbol is preserved exactly so codegen- // generated callsites resolve at link time. +#[cfg(feature = "ffi-exports")] pub mod ffi; diff --git a/crates/perry-ui-windows/src/state.rs b/crates/perry-ui-windows/src/state.rs index e3868b4ae8..1816d2af07 100644 --- a/crates/perry-ui-windows/src/state.rs +++ b/crates/perry-ui-windows/src/state.rs @@ -275,25 +275,9 @@ pub fn state_set(handle: i64, value: f64) { // Format as number formatted.clone() }; - #[cfg(target_os = "windows")] - { - if let Some(hwnd) = widgets::get_hwnd(binding.textfield_handle) { - binding.suppress.set(true); - let wide: Vec = - text.encode_utf16().chain(std::iter::once(0)).collect(); - unsafe { - let _ = windows::Win32::UI::WindowsAndMessaging::SetWindowTextW( - hwnd, - windows::core::PCWSTR(wide.as_ptr()), - ); - } - binding.suppress.set(false); - } - } - #[cfg(not(target_os = "windows"))] - { - let _ = text; - } + binding.suppress.set(true); + widgets::textfield::set_string_str(binding.textfield_handle, &text); + binding.suppress.set(false); } } }); @@ -487,21 +471,6 @@ pub fn bind_textfield(state_handle: i64, textfield_handle: i64) { let str_ptr = unsafe { js_get_string_pointer_unified(value) }; if !str_ptr.is_null() { let text = unsafe { str_from_header(str_ptr) }; - #[cfg(target_os = "windows")] - { - if let Some(hwnd) = widgets::get_hwnd(textfield_handle) { - let wide: Vec = text.encode_utf16().chain(std::iter::once(0)).collect(); - unsafe { - let _ = windows::Win32::UI::WindowsAndMessaging::SetWindowTextW( - hwnd, - windows::core::PCWSTR(wide.as_ptr()), - ); - } - } - } - #[cfg(not(target_os = "windows"))] - { - let _ = text; - } + widgets::textfield::set_string_str(textfield_handle, &text); } } diff --git a/crates/perry-ui-windows/src/widgets/textfield.rs b/crates/perry-ui-windows/src/widgets/textfield.rs index 696fb5083c..1e47ff4361 100644 --- a/crates/perry-ui-windows/src/widgets/textfield.rs +++ b/crates/perry-ui-windows/src/widgets/textfield.rs @@ -343,12 +343,16 @@ pub fn get_string(handle: i64) -> i64 { /// Set the text value of a TextField programmatically. pub fn set_string_value(handle: i64, text_ptr: *const u8) { let text = unsafe { str_from_header(text_ptr) }; + set_string_str(handle, &text); +} +/// Set the text value from backend-neutral state binding code. +pub fn set_string_str(handle: i64, text: &str) { #[cfg(target_os = "windows")] { if let Some(hwnd) = super::get_hwnd(handle) { SUPPRESS_CHANGE.with(|s| *s.borrow_mut() = true); - let wide = to_wide(&text); + let wide = to_wide(text); unsafe { let _ = SetWindowTextW(hwnd, windows::core::PCWSTR(wide.as_ptr())); } diff --git a/crates/perry/src/commands/compile/library_search.rs b/crates/perry/src/commands/compile/library_search.rs index a538ddea72..b0a242c857 100644 --- a/crates/perry/src/commands/compile/library_search.rs +++ b/crates/perry/src/commands/compile/library_search.rs @@ -1364,9 +1364,8 @@ pub(super) fn find_ui_library(target: Option<&str>) -> Option { Some("tvos-simulator") | Some("tvos") => "libperry_ui_tvos.a", Some("linux") => "libperry_ui_gtk4.a", Some("macos") => "libperry_ui_macos.a", - // Opt-in WinUI 3 backend (#4680) — its own staticlib. It bundles the - // perry-ui-windows Win32 symbols today (scaffold), so the FFI surface - // is identical to the `windows` lib. + // Opt-in WinUI 3 backend (#4680) with the same Perry FFI surface as + // the default Win32 library. Some("windows-winui") => "perry_ui_windows_winui.lib", target if is_windows_target(target) => "perry_ui_windows.lib", _ => { diff --git a/crates/perry/src/commands/compile/link/build_and_run.rs b/crates/perry/src/commands/compile/link/build_and_run.rs index b7384fdfa4..187ff245e1 100644 --- a/crates/perry/src/commands/compile/link/build_and_run.rs +++ b/crates/perry/src/commands/compile/link/build_and_run.rs @@ -971,6 +971,11 @@ pub(crate) fn build_and_run_link( find_ui_library(target) }; if let Some(ui_lib) = ui_lib_option { + let winui_import_library = if matches!(target, Some("windows-winui")) { + Some(winui_assets::winui_bootstrap_import_library(&ui_lib)?) + } else { + None + }; // The UI staticlib bundles perry_runtime + Rust std. When perry-stdlib // is also linked (which bundles the same), duplicate symbols cause // crashes (conflicting static state initialization). Strip fully @@ -1048,6 +1053,9 @@ pub(crate) fn build_and_run_link( // undefined when the lib is scanned. /WHOLEARCHIVE forces all // objects from the archive to be included unconditionally. cmd.arg(format!("/WHOLEARCHIVE:{}", ui_lib.display())); + if let Some(import_library) = winui_import_library { + cmd.arg(import_library); + } } else { cmd.arg(&ui_lib); } @@ -1890,6 +1898,7 @@ pub(crate) fn build_and_run_link( if let Some(path) = embedded_info_plist_path { let _ = fs::remove_file(path); } + winui_assets::deploy_winui_runtime_assets(target, ctx.needs_ui, exe_path)?; return Ok(link_cache_status); } @@ -1955,5 +1964,7 @@ pub(crate) fn build_and_run_link( return Err(anyhow!("Linking failed")); } + winui_assets::deploy_winui_runtime_assets(target, ctx.needs_ui, exe_path)?; + Ok(link_cache_status) } diff --git a/crates/perry/src/commands/compile/link/mod.rs b/crates/perry/src/commands/compile/link/mod.rs index 1fd3c699ec..e8ff83b0f1 100644 --- a/crates/perry/src/commands/compile/link/mod.rs +++ b/crates/perry/src/commands/compile/link/mod.rs @@ -53,6 +53,7 @@ mod pkg_config; mod platform_cmd; mod watchos_frameworks; mod windows_link; +mod winui_assets; use archive_cache::{prepare_well_known_archives, PreparedArchiveInputs}; pub(super) use build_and_run::build_and_run_link; diff --git a/crates/perry/src/commands/compile/link/winui_assets.rs b/crates/perry/src/commands/compile/link/winui_assets.rs new file mode 100644 index 0000000000..d874d85546 --- /dev/null +++ b/crates/perry/src/commands/compile/link/winui_assets.rs @@ -0,0 +1,147 @@ +//! WinUI 3 runtime-asset deployment for the executable link. +//! +//! Split out of `build_and_run.rs` (2000-line-per-file cap). Pure relocation +//! of the `windows-winui` bootstrap-import-library lookup and the runtime +//! asset copy that runs after a successful link, plus their tests. + +use super::*; +use anyhow::Context; + +const WINUI_RUNTIME_ASSETS: [&str; 2] = + ["Microsoft.WindowsAppRuntime.Bootstrap.dll", "resources.pri"]; +const WINUI_BOOTSTRAP_IMPORT_LIBRARY: &str = "Microsoft.WindowsAppRuntime.Bootstrap.lib"; + +pub(super) fn winui_bootstrap_import_library(ui_library: &Path) -> Result { + let source_dir = ui_library.parent().ok_or_else(|| { + anyhow!( + "WinUI library has no parent directory: {}", + ui_library.display() + ) + })?; + let import_library = source_dir.join(WINUI_BOOTSTRAP_IMPORT_LIBRARY); + if !import_library.is_file() { + return Err(anyhow!( + "WinUI bootstrap import library {} is missing next to {}. Rebuild with: cargo build --release -p perry-ui-windows-winui", + WINUI_BOOTSTRAP_IMPORT_LIBRARY, + ui_library.display() + )); + } + Ok(import_library) +} + +fn copy_winui_runtime_assets(ui_library: &Path, exe_path: &Path) -> Result<()> { + let source_dir = ui_library.parent().ok_or_else(|| { + anyhow!( + "WinUI library has no parent directory: {}", + ui_library.display() + ) + })?; + let destination_dir = exe_path.parent().ok_or_else(|| { + anyhow!( + "WinUI executable has no parent directory: {}", + exe_path.display() + ) + })?; + fs::create_dir_all(destination_dir)?; + + for asset in WINUI_RUNTIME_ASSETS { + let source = source_dir.join(asset); + if !source.is_file() { + return Err(anyhow!( + "WinUI runtime asset {} is missing next to {}. Rebuild with: cargo build --release -p perry-ui-windows-winui", + asset, + ui_library.display() + )); + } + let destination = destination_dir.join(asset); + if source != destination { + fs::copy(&source, &destination).with_context(|| { + format!( + "failed to deploy WinUI runtime asset {} to {}", + source.display(), + destination.display() + ) + })?; + } + } + Ok(()) +} + +pub(super) fn deploy_winui_runtime_assets( + target: Option<&str>, + needs_ui: bool, + exe_path: &Path, +) -> Result<()> { + if !needs_ui || !matches!(target, Some("windows-winui")) { + return Ok(()); + } + let ui_library = find_ui_library(target) + .ok_or_else(|| anyhow!("WinUI library disappeared before runtime asset deployment"))?; + copy_winui_runtime_assets(&ui_library, exe_path) +} + +#[cfg(test)] +mod winui_asset_tests { + use super::*; + use std::time::{SystemTime, UNIX_EPOCH}; + + fn test_root() -> PathBuf { + let nonce = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_nanos(); + std::env::temp_dir().join(format!("perry-winui-assets-{}-{nonce}", std::process::id())) + } + + #[test] + fn copies_winui_runtime_assets_next_to_executable() { + let root = test_root(); + let source_dir = root.join("lib"); + let output_dir = root.join("dist"); + fs::create_dir_all(&source_dir).unwrap(); + let ui_library = source_dir.join("perry_ui_windows_winui.lib"); + fs::write(&ui_library, b"archive").unwrap(); + for asset in WINUI_RUNTIME_ASSETS { + fs::write(source_dir.join(asset), asset.as_bytes()).unwrap(); + } + + let exe_path = output_dir.join("todo.exe"); + copy_winui_runtime_assets(&ui_library, &exe_path).unwrap(); + + for asset in WINUI_RUNTIME_ASSETS { + assert_eq!(fs::read(output_dir.join(asset)).unwrap(), asset.as_bytes()); + } + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn locates_winui_bootstrap_import_library_next_to_ui_archive() { + let root = test_root(); + fs::create_dir_all(&root).unwrap(); + let ui_library = root.join("perry_ui_windows_winui.lib"); + let import_library = root.join(WINUI_BOOTSTRAP_IMPORT_LIBRARY); + fs::write(&ui_library, b"archive").unwrap(); + fs::write(&import_library, b"import library").unwrap(); + + assert_eq!( + winui_bootstrap_import_library(&ui_library).unwrap(), + import_library + ); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn reports_a_missing_winui_runtime_asset() { + let root = test_root(); + let source_dir = root.join("lib"); + fs::create_dir_all(&source_dir).unwrap(); + let ui_library = source_dir.join("perry_ui_windows_winui.lib"); + fs::write(&ui_library, b"archive").unwrap(); + + let error = copy_winui_runtime_assets(&ui_library, &root.join("todo.exe")) + .unwrap_err() + .to_string(); + assert!(error.contains("Microsoft.WindowsAppRuntime.Bootstrap.dll")); + fs::remove_dir_all(root).unwrap(); + } +} diff --git a/docs/examples/ui/state/todo_app.ts b/docs/examples/ui/state/todo_app.ts index c96e77d955..11bdb84b65 100644 --- a/docs/examples/ui/state/todo_app.ts +++ b/docs/examples/ui/state/todo_app.ts @@ -1,6 +1,6 @@ // demonstrates: complete reactive todo app combining State, ForEach, and widget tree mutation // docs: docs/src/ui/state.md -// platforms: macos, linux, windows +// platforms: macos, linux, windows, windows-winui // targets: ios-simulator, tvos-simulator, watchos-simulator, web, wasm import { diff --git a/docs/src/cli/flags.md b/docs/src/cli/flags.md index 0ee22336f2..a3b09b9ad2 100644 --- a/docs/src/cli/flags.md +++ b/docs/src/cli/flags.md @@ -42,7 +42,7 @@ Use `--target` to cross-compile: | `windows` | Windows | Win32/GDI executable for the native Windows host architecture; x64 when cross-compiling from another OS | | `windows-x86_64` | Windows x64 | Explicit x64 MSVC target | | `windows-aarch64` | Windows ARM64 | Explicit ARM64 MSVC target (`windows-arm64` is accepted as an alias) | -| `windows-winui` | Windows (Fluent) | Opt-in WinUI 3 / Fluent backend (#4680). **Scaffold:** currently renders via Win32 while the XAML widget mapping lands incrementally; selects the `perry-ui-windows-winui` static library. Build that lib first: `cargo build --release -p perry-ui-windows-winui`. | +| `windows-winui` | Windows (Fluent) | Opt-in WinUI 3 / Fluent backend for core Perry controls. Requires the Windows App SDK 2.0 runtime; see [Windows](../platforms/windows.md#winui-3-fluent-target). | | `linux` | Linux | GTK4 executable | ## Output Types diff --git a/docs/src/platforms/windows.md b/docs/src/platforms/windows.md index 6be9f11b14..bb781ac025 100644 --- a/docs/src/platforms/windows.md +++ b/docs/src/platforms/windows.md @@ -62,6 +62,41 @@ Perry maps UI widgets to Win32 controls: | Canvas | GDI drawing | | Form/Section | GroupBox | +## WinUI 3 (Fluent) target + +`--target windows-winui` is an opt-in backend for apps that want native WinUI +3 controls and Fluent window chrome. The default `--target windows` path is +unchanged and continues to use Win32/GDI. + +The Fluent backend currently maps Text, Button, TextField, SecureField, +Toggle, Slider, ProgressView, VStack, HStack, ZStack, Spacer, Divider, +ScrollView, Form, Section, and LazyVStack through Windows Reactor. Platform +services continue to reuse the established Win32 implementation. Other visual +controls retain their ABI entry points and will gain Fluent mappings +incrementally. + +Perry emits a framework-dependent, unpackaged WinUI app. Install the stable +[Windows App SDK 2.0 runtime](https://learn.microsoft.com/windows/apps/windows-app-sdk/downloads) +for the executable's architecture (x64 or ARM64) on every target machine. +Version 2.0.1 or newer is accepted. Microsoft documents the runtime and +bootstrap requirements in its +[deployment guide for unpackaged apps](https://learn.microsoft.com/windows/apps/windows-app-sdk/deploy-unpackaged-apps). + +Build and run the ToDo sample from a Perry checkout: + +```powershell +cargo build --release -p perry-ui-windows-winui +cargo run --release -p perry -- compile docs/examples/ui/state/todo_app.ts ` + --target windows-winui -o todo-winui.exe +.\todo-winui.exe +``` + +The compiler copies `Microsoft.WindowsAppRuntime.Bootstrap.dll` and +`resources.pri` beside the generated executable, including on link-cache hits. +Keep both files next to the `.exe` when redistributing it. If the Windows App +SDK runtime cannot be initialized, Perry reports the reason when +`PERRY_WINUI_DIAG=1` is set and uses the Win32 backend instead. + ## Windows-Specific APIs - **Menu bar**: HMENU / SetMenu diff --git a/scripts/check_file_size.sh b/scripts/check_file_size.sh index d8d8ac58b1..00be2b3dc6 100755 --- a/scripts/check_file_size.sh +++ b/scripts/check_file_size.sh @@ -34,6 +34,12 @@ # auxiliary enums and impls were already peeled into siblings; # the variant list itself is irreducible. # +# Vendored third-party sources are also allowlisted (currently +# third_party/windows-winui/*): they are an upstream snapshot, not +# Perry review surface, and must stay byte-identical so the vendor +# drop can be re-diffed. Each still carries its own one-line +# rationale in the ALLOWLIST block below. +# set -euo pipefail THRESHOLD="${PERRY_FILE_SIZE_THRESHOLD:-2000}" @@ -136,6 +142,21 @@ crates/perry-stdlib/src/streams.rs # phase/debt/budget groups should be split together in the tracked #1435 file # decomposition rather than mixed into an unrelated runtime fast-path PR. crates/perry-runtime/src/gc/policy.rs +# --- Vendored third-party sources (third_party/windows-winui/, see its +# VENDORED.md): a verbatim snapshot of Microsoft windows-rs / Windows Reactor +# at commit 65066a7109c214f317ed66261cfb7518160b8aaf. These are NOT Perry +# review surface and must stay byte-identical to upstream so the snapshot can +# be re-diffed and re-pulled; splitting them would fork them permanently. --- +# Machine-generated WinRT metadata bindings for Windows.Foundation.Collections. +third_party/windows-winui/windows-collections/src/bindings.rs +# Machine-generated WinRT metadata bindings for Windows.Foundation (IAsyncInfo). +third_party/windows-winui/windows-future/src/bindings.rs +# Machine-generated WinRT metadata bindings for Microsoft.UI.Xaml — the whole +# WinUI 3 control surface in one generated file (26k lines). +third_party/windows-winui/windows-reactor/src/bindings.rs +# Upstream Windows Reactor XAML backend: the single Element -> Microsoft.UI.Xaml +# realization/diff trunk shipped as one module by upstream. +third_party/windows-winui/windows-reactor/src/winui/backend/mod.rs EOF ) diff --git a/scripts/gc_runtime_root_holders.json b/scripts/gc_runtime_root_holders.json index 9515f1e9b8..e39071bd95 100644 --- a/scripts/gc_runtime_root_holders.json +++ b/scripts/gc_runtime_root_holders.json @@ -1486,6 +1486,12 @@ "verdict": "not_a_gc_pointer", "why": "Text registry maps Rust string ids to numeric native widget handles; it contains no JavaScript value." }, + { + "file": "crates/perry-ui-windows-winui/src/app.rs", + "name": "APPS", + "verdict": "not_a_gc_pointer", + "why": "AppState holds a Rust-owned title String, the two f64 window dimensions, an i64 WIDGET handle (root: a 1-based index into widgets::NODES, not an address), two Option<(f64, f64)> size constraints and a PresenterKind enum \u2014 no NaN-boxed JavaScript value, so rule S fired on the f64/i64 fields rather than on a heap pointer. This module's real callback roots (ON_ACTIVATE / ON_TERMINATE / PENDING_TIMERS, each a raw closure pointer unboxed by js_nanbox_get_pointer) are visited by scan_winui_app_gc_roots." + }, { "file": "crates/perry-ui-windows/src/app.rs", "name": "APPS", diff --git a/third_party/windows-winui/VENDORED.md b/third_party/windows-winui/VENDORED.md new file mode 100644 index 0000000000..1d1414f5da --- /dev/null +++ b/third_party/windows-winui/VENDORED.md @@ -0,0 +1,18 @@ +# Vendored Windows Reactor snapshot + +This directory contains the Windows Reactor crate and the matching unreleased +`windows-*` support sources needed by Perry's WinUI 3 backend. The source was +copied from Microsoft `windows-rs` commit +`65066a7109c214f317ed66261cfb7518160b8aaf` (the merge commit for +`microsoft/windows-rs#4479`). + +The manifests only differ where needed to replace the upstream workspace +dependencies with local paths and published Rust ecosystem dependencies. The +Reactor build script is also adapted to stage the framework-dependent +bootstrap DLL, its import library, and the XAML resource PRI in Perry's Cargo +target directory. Perry also adds an application-exit callback hook so its +existing lifecycle ABI can run termination handlers when the WinUI window +closes. + +The upstream MIT and Apache-2.0 license files are preserved in every vendored +crate directory. diff --git a/third_party/windows-winui/windows-collections/Cargo.toml b/third_party/windows-winui/windows-collections/Cargo.toml new file mode 100644 index 0000000000..67426e3435 --- /dev/null +++ b/third_party/windows-winui/windows-collections/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "windows-collections" +version = "0.3.2" +edition = "2021" +rust-version = "1.82" +license = "MIT OR Apache-2.0" +description = "Windows collection types" +repository = "https://github.com/microsoft/windows-rs" +categories = ["os::windows-apis"] +readme = "readme.md" + +[dependencies] +windows-core = { path = "../windows-core", default-features = false } + +[dev-dependencies] +windows-strings = { path = "../windows-strings" } + +[features] +default = ["std"] +std = ["windows-core/std"] + +[package.metadata.docs.rs] +targets = [] diff --git a/third_party/windows-winui/windows-collections/license-apache-2.0 b/third_party/windows-winui/windows-collections/license-apache-2.0 new file mode 100644 index 0000000000..b5ed4ecec2 --- /dev/null +++ b/third_party/windows-winui/windows-collections/license-apache-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/windows-winui/windows-collections/license-mit b/third_party/windows-winui/windows-collections/license-mit new file mode 100644 index 0000000000..9e841e7a26 --- /dev/null +++ b/third_party/windows-winui/windows-collections/license-mit @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/third_party/windows-winui/windows-collections/readme.md b/third_party/windows-winui/windows-collections/readme.md new file mode 100644 index 0000000000..596a4295df --- /dev/null +++ b/third_party/windows-winui/windows-collections/readme.md @@ -0,0 +1,46 @@ +## Windows collection types + +The [windows-collections](https://crates.io/crates/windows-collections) crate provides stock collection support for Windows APIs. + +* [Getting started](https://kennykerr.ca/rust-getting-started/) +* [Samples](https://github.com/microsoft/windows-rs/tree/master/crates/samples) +* [Releases](https://github.com/microsoft/windows-rs/releases) + +Start by adding the following to your Cargo.toml file: + +```toml +[dependencies.windows-collections] +version = "0.3" +``` + +Use the Windows collection types as needed: + +```rust,ignore +use windows_collections::*; + +let numbers = IIterable::::from(vec![1, 2, 3]); + +for value in numbers { + println!("{value}"); +} +``` + +Naturally, the Windows collection types work with other Windows crates: + +```rust,ignore +use windows_collections::*; +use windows_strings::*; + +let greetings = + IVectorView::::from(vec![HSTRING::from("hello"), HSTRING::from("world")]); + +for value in greetings { + println!("{value:?}"); +} + +let map = std::collections::BTreeMap::from([("one".into(), 1), ("two".into(), 2)]); +let map = IMapView::::from(map); + +assert_eq!(map.Lookup(h!("one")).unwrap(), 1); +assert_eq!(map.Lookup(h!("two")).unwrap(), 2); +``` diff --git a/third_party/windows-winui/windows-collections/src/bindings.rs b/third_party/windows-winui/windows-collections/src/bindings.rs new file mode 100644 index 0000000000..6950481a7f --- /dev/null +++ b/third_party/windows-winui/windows-collections/src/bindings.rs @@ -0,0 +1,3094 @@ +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct CollectionChange(pub i32); +impl CollectionChange { + pub const Reset: Self = Self(0i32); + pub const ItemInserted: Self = Self(1i32); + pub const ItemRemoved: Self = Self(2i32); + pub const ItemChanged: Self = Self(3i32); +} +impl windows_core::TypeKind for CollectionChange { + type TypeKind = windows_core::CopyType; +} +impl windows_core::RuntimeType for CollectionChange { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::from_slice( + b"enum(Windows.Foundation.Collections.CollectionChange;i4)", + ); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::from_slice( + b"Windows.Foundation.Collections.CollectionChange", + ); +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IIterable(windows_core::IUnknown, core::marker::PhantomData) +where + T: windows_core::RuntimeType + 'static; +impl windows_core::imp::CanInto + for IIterable +{ +} +impl windows_core::imp::CanInto + for IIterable +{ +} +unsafe impl windows_core::Interface for IIterable { + type Vtable = IIterable_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl windows_core::RuntimeType for IIterable { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({faa585ea-6214-4217-afda-7f46de5869b3}") + .push_slice(b";") + .push_other(T::SIGNATURE) + .push_slice(b")"); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"Windows.Foundation.Collections.IIterable`1<") + .push_other(T::NAME) + .push_slice(b">"); +} +impl IIterable { + pub fn First(&self) -> windows_core::Result> { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).First)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl windows_core::RuntimeName for IIterable { + const NAME: &'static str = "Windows.Foundation.Collections.IIterable"; + const RUNTIME_CLASS_NAME: windows_core::imp::ConstBuffer = + ::NAME; +} +pub trait IIterable_Impl: windows_core::IUnknownImpl +where + T: windows_core::RuntimeType + 'static, +{ + fn First(&self) -> windows_core::Result>; +} +impl IIterable_Vtbl { + pub const fn new, const OFFSET: isize>() -> Self { + unsafe extern "system" fn First< + T: windows_core::RuntimeType + 'static, + Identity: IIterable_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IIterable_Impl::First(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::, OFFSET>(), + First: First::, + T: core::marker::PhantomData::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == & as windows_core::Interface>::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IIterable_Vtbl +where + T: windows_core::RuntimeType + 'static, +{ + pub base__: windows_core::IInspectable_Vtbl, + pub First: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + T: core::marker::PhantomData, +} +impl IntoIterator for IIterable { + type Item = T; + type IntoIter = IIterator; + fn into_iter(self) -> Self::IntoIter { + IntoIterator::into_iter(&self) + } +} +impl IntoIterator for &IIterable { + type Item = T; + type IntoIter = IIterator; + fn into_iter(self) -> Self::IntoIter { + self.First().unwrap() + } +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IIterator(windows_core::IUnknown, core::marker::PhantomData) +where + T: windows_core::RuntimeType + 'static; +impl windows_core::imp::CanInto + for IIterator +{ +} +impl windows_core::imp::CanInto + for IIterator +{ +} +unsafe impl windows_core::Interface for IIterator { + type Vtable = IIterator_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl windows_core::RuntimeType for IIterator { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({6a79e863-4300-459a-9966-cbb660963ee1}") + .push_slice(b";") + .push_other(T::SIGNATURE) + .push_slice(b")"); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"Windows.Foundation.Collections.IIterator`1<") + .push_other(T::NAME) + .push_slice(b">"); +} +impl IIterator { + pub fn Current(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Current)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn HasCurrent(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).HasCurrent)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .map(|| result__) + } + } + pub fn MoveNext(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).MoveNext)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .map(|| result__) + } + } + pub fn GetMany( + &self, + items: &mut [>::Default], + ) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).GetMany)( + windows_core::Interface::as_raw(self), + items.len().try_into().unwrap(), + core::mem::transmute_copy(&items), + &mut result__, + ) + .map(|| result__) + } + } +} +impl windows_core::RuntimeName for IIterator { + const NAME: &'static str = "Windows.Foundation.Collections.IIterator"; + const RUNTIME_CLASS_NAME: windows_core::imp::ConstBuffer = + ::NAME; +} +pub trait IIterator_Impl: windows_core::IUnknownImpl +where + T: windows_core::RuntimeType + 'static, +{ + fn Current(&self) -> windows_core::Result; + fn HasCurrent(&self) -> windows_core::Result; + fn MoveNext(&self) -> windows_core::Result; + fn GetMany( + &self, + items: &mut [>::Default], + ) -> windows_core::Result; +} +impl IIterator_Vtbl { + pub const fn new, const OFFSET: isize>() -> Self { + unsafe extern "system" fn Current< + T: windows_core::RuntimeType + 'static, + Identity: IIterator_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IIterator_Impl::Current(this) { + 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 HasCurrent< + T: windows_core::RuntimeType + 'static, + Identity: IIterator_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut bool, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IIterator_Impl::HasCurrent(this) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn MoveNext< + T: windows_core::RuntimeType + 'static, + Identity: IIterator_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut bool, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IIterator_Impl::MoveNext(this) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn GetMany< + T: windows_core::RuntimeType + 'static, + Identity: IIterator_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + items_array_size: u32, + items: *mut windows_core::AbiType, + result__: *mut u32, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IIterator_Impl::GetMany( + this, + core::slice::from_raw_parts_mut( + core::mem::transmute_copy(&items), + items_array_size as usize, + ), + ) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::, OFFSET>(), + Current: Current::, + HasCurrent: HasCurrent::, + MoveNext: MoveNext::, + GetMany: GetMany::, + T: core::marker::PhantomData::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == & as windows_core::Interface>::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IIterator_Vtbl +where + T: windows_core::RuntimeType + 'static, +{ + pub base__: windows_core::IInspectable_Vtbl, + pub Current: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut windows_core::AbiType, + ) -> windows_core::HRESULT, + pub HasCurrent: + unsafe extern "system" fn(*mut core::ffi::c_void, *mut bool) -> windows_core::HRESULT, + pub MoveNext: + unsafe extern "system" fn(*mut core::ffi::c_void, *mut bool) -> windows_core::HRESULT, + pub GetMany: unsafe extern "system" fn( + *mut core::ffi::c_void, + u32, + *mut windows_core::AbiType, + *mut u32, + ) -> windows_core::HRESULT, + T: core::marker::PhantomData, +} +impl Iterator for IIterator { + type Item = T; + fn next(&mut self) -> Option { + let result = if self.HasCurrent().unwrap_or(false) { + self.Current().ok() + } else { + None + }; + if result.is_some() { + let _ = self.MoveNext(); + } + result + } +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IKeyValuePair( + windows_core::IUnknown, + core::marker::PhantomData, + core::marker::PhantomData, +) +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static; +impl + windows_core::imp::CanInto for IKeyValuePair +{ +} +impl + windows_core::imp::CanInto for IKeyValuePair +{ +} +unsafe impl + windows_core::Interface for IKeyValuePair +{ + type Vtable = IKeyValuePair_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl + windows_core::RuntimeType for IKeyValuePair +{ + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({02b51929-c1c4-4a7e-8940-0312b5c18500}") + .push_slice(b";") + .push_other(K::SIGNATURE) + .push_slice(b";") + .push_other(V::SIGNATURE) + .push_slice(b")"); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"Windows.Foundation.Collections.IKeyValuePair`2<") + .push_other(K::NAME) + .push_slice(b", ") + .push_other(V::NAME) + .push_slice(b">"); +} +impl + IKeyValuePair +{ + pub fn Key(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Key)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn Value(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Value)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl + windows_core::RuntimeName for IKeyValuePair +{ + const NAME: &'static str = "Windows.Foundation.Collections.IKeyValuePair"; + const RUNTIME_CLASS_NAME: windows_core::imp::ConstBuffer = + ::NAME; +} +pub trait IKeyValuePair_Impl: windows_core::IUnknownImpl +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, +{ + fn Key(&self) -> windows_core::Result; + fn Value(&self) -> windows_core::Result; +} +impl + IKeyValuePair_Vtbl +{ + pub const fn new, const OFFSET: isize>() -> Self { + unsafe extern "system" fn Key< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IKeyValuePair_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IKeyValuePair_Impl::Key(this) { + 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 Value< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IKeyValuePair_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IKeyValuePair_Impl::Value(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::, OFFSET>(), + Key: Key::, + Value: Value::, + K: core::marker::PhantomData::, + V: core::marker::PhantomData::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == & as windows_core::Interface>::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IKeyValuePair_Vtbl +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, +{ + pub base__: windows_core::IInspectable_Vtbl, + pub Key: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut windows_core::AbiType, + ) -> windows_core::HRESULT, + pub Value: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut windows_core::AbiType, + ) -> windows_core::HRESULT, + K: core::marker::PhantomData, + V: core::marker::PhantomData, +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IMap( + windows_core::IUnknown, + core::marker::PhantomData, + core::marker::PhantomData, +) +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static; +impl + windows_core::imp::CanInto for IMap +{ +} +impl + windows_core::imp::CanInto for IMap +{ +} +unsafe impl + windows_core::Interface for IMap +{ + type Vtable = IMap_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl + windows_core::RuntimeType for IMap +{ + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({3c2925fe-8519-45c1-aa79-197b6718c1c1}") + .push_slice(b";") + .push_other(K::SIGNATURE) + .push_slice(b";") + .push_other(V::SIGNATURE) + .push_slice(b")"); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"Windows.Foundation.Collections.IMap`2<") + .push_other(K::NAME) + .push_slice(b", ") + .push_other(V::NAME) + .push_slice(b">"); +} +impl + windows_core::imp::CanInto>> for IMap +{ + const QUERY: bool = true; +} +impl IMap { + pub fn Lookup(&self, key: P0) -> windows_core::Result + where + P0: windows_core::Param, + { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Lookup)( + windows_core::Interface::as_raw(self), + key.param().abi(), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn Size(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Size)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .map(|| result__) + } + } + pub fn HasKey(&self, key: P0) -> windows_core::Result + where + P0: windows_core::Param, + { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).HasKey)( + windows_core::Interface::as_raw(self), + key.param().abi(), + &mut result__, + ) + .map(|| result__) + } + } + pub fn GetView(&self) -> windows_core::Result> { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).GetView)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn Insert(&self, key: P0, value: P1) -> windows_core::Result + where + P0: windows_core::Param, + P1: windows_core::Param, + { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Insert)( + windows_core::Interface::as_raw(self), + key.param().abi(), + value.param().abi(), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Remove(&self, key: P0) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + unsafe { + (windows_core::Interface::vtable(self).Remove)( + windows_core::Interface::as_raw(self), + key.param().abi(), + ) + .ok() + } + } + pub fn Clear(&self) -> windows_core::Result<()> { + unsafe { + (windows_core::Interface::vtable(self).Clear)(windows_core::Interface::as_raw(self)) + .ok() + } + } + pub fn First(&self) -> windows_core::Result>> { + let this = &windows_core::Interface::cast::>>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).First)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl IntoIterator + for IMap +{ + type Item = IKeyValuePair; + type IntoIter = IIterator; + fn into_iter(self) -> Self::IntoIter { + IntoIterator::into_iter(&self) + } +} +impl IntoIterator + for &IMap +{ + type Item = IKeyValuePair; + type IntoIter = IIterator; + fn into_iter(self) -> Self::IntoIter { + self.First().unwrap() + } +} +impl + windows_core::RuntimeName for IMap +{ + const NAME: &'static str = "Windows.Foundation.Collections.IMap"; + const RUNTIME_CLASS_NAME: windows_core::imp::ConstBuffer = + ::NAME; +} +pub trait IMap_Impl: IIterable_Impl> +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, +{ + fn Lookup(&self, key: windows_core::Ref) -> windows_core::Result; + fn Size(&self) -> windows_core::Result; + fn HasKey(&self, key: windows_core::Ref) -> windows_core::Result; + fn GetView(&self) -> windows_core::Result>; + fn Insert( + &self, + key: windows_core::Ref, + value: windows_core::Ref, + ) -> windows_core::Result; + fn Remove(&self, key: windows_core::Ref) -> windows_core::Result<()>; + fn Clear(&self) -> windows_core::Result<()>; +} +impl + IMap_Vtbl +{ + pub const fn new, const OFFSET: isize>() -> Self { + unsafe extern "system" fn Lookup< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IMap_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + key: windows_core::AbiType, + result__: *mut windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IMap_Impl::Lookup(this, core::mem::transmute_copy(&key)) { + 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 Size< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IMap_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut u32, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IMap_Impl::Size(this) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn HasKey< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IMap_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + key: windows_core::AbiType, + result__: *mut bool, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IMap_Impl::HasKey(this, core::mem::transmute_copy(&key)) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn GetView< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IMap_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IMap_Impl::GetView(this) { + 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 Insert< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IMap_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + key: windows_core::AbiType, + value: windows_core::AbiType, + result__: *mut bool, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IMap_Impl::Insert( + this, + core::mem::transmute_copy(&key), + core::mem::transmute_copy(&value), + ) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn Remove< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IMap_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + key: windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IMap_Impl::Remove(this, core::mem::transmute_copy(&key)).into() + } + } + unsafe extern "system" fn Clear< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IMap_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IMap_Impl::Clear(this).into() + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::, OFFSET>(), + Lookup: Lookup::, + Size: Size::, + HasKey: HasKey::, + GetView: GetView::, + Insert: Insert::, + Remove: Remove::, + Clear: Clear::, + K: core::marker::PhantomData::, + V: core::marker::PhantomData::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == & as windows_core::Interface>::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IMap_Vtbl +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, +{ + pub base__: windows_core::IInspectable_Vtbl, + pub Lookup: unsafe extern "system" fn( + *mut core::ffi::c_void, + windows_core::AbiType, + *mut windows_core::AbiType, + ) -> windows_core::HRESULT, + pub Size: unsafe extern "system" fn(*mut core::ffi::c_void, *mut u32) -> windows_core::HRESULT, + pub HasKey: unsafe extern "system" fn( + *mut core::ffi::c_void, + windows_core::AbiType, + *mut bool, + ) -> windows_core::HRESULT, + pub GetView: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub Insert: unsafe extern "system" fn( + *mut core::ffi::c_void, + windows_core::AbiType, + windows_core::AbiType, + *mut bool, + ) -> windows_core::HRESULT, + pub Remove: unsafe extern "system" fn( + *mut core::ffi::c_void, + windows_core::AbiType, + ) -> windows_core::HRESULT, + pub Clear: unsafe extern "system" fn(*mut core::ffi::c_void) -> windows_core::HRESULT, + K: core::marker::PhantomData, + V: core::marker::PhantomData, +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IMapChangedEventArgs(windows_core::IUnknown, core::marker::PhantomData) +where + K: windows_core::RuntimeType + 'static; +impl windows_core::imp::CanInto + for IMapChangedEventArgs +{ +} +impl windows_core::imp::CanInto + for IMapChangedEventArgs +{ +} +unsafe impl windows_core::Interface + for IMapChangedEventArgs +{ + type Vtable = IMapChangedEventArgs_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl windows_core::RuntimeType for IMapChangedEventArgs { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({9939f4df-050a-4c0f-aa60-77075f9c4777}") + .push_slice(b";") + .push_other(K::SIGNATURE) + .push_slice(b")"); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"Windows.Foundation.Collections.IMapChangedEventArgs`1<") + .push_other(K::NAME) + .push_slice(b">"); +} +impl IMapChangedEventArgs { + pub fn CollectionChange(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).CollectionChange)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Key(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Key)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl windows_core::RuntimeName for IMapChangedEventArgs { + const NAME: &'static str = "Windows.Foundation.Collections.IMapChangedEventArgs"; + const RUNTIME_CLASS_NAME: windows_core::imp::ConstBuffer = + ::NAME; +} +pub trait IMapChangedEventArgs_Impl: windows_core::IUnknownImpl +where + K: windows_core::RuntimeType + 'static, +{ + fn CollectionChange(&self) -> windows_core::Result; + fn Key(&self) -> windows_core::Result; +} +impl IMapChangedEventArgs_Vtbl { + pub const fn new, const OFFSET: isize>() -> Self { + unsafe extern "system" fn CollectionChange< + K: windows_core::RuntimeType + 'static, + Identity: IMapChangedEventArgs_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut CollectionChange, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IMapChangedEventArgs_Impl::CollectionChange(this) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn Key< + K: windows_core::RuntimeType + 'static, + Identity: IMapChangedEventArgs_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IMapChangedEventArgs_Impl::Key(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::, OFFSET>( + ), + CollectionChange: CollectionChange::, + Key: Key::, + K: core::marker::PhantomData::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == & as windows_core::Interface>::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IMapChangedEventArgs_Vtbl +where + K: windows_core::RuntimeType + 'static, +{ + pub base__: windows_core::IInspectable_Vtbl, + pub CollectionChange: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut CollectionChange, + ) -> windows_core::HRESULT, + pub Key: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut windows_core::AbiType, + ) -> windows_core::HRESULT, + K: core::marker::PhantomData, +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IMapView( + windows_core::IUnknown, + core::marker::PhantomData, + core::marker::PhantomData, +) +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static; +impl + windows_core::imp::CanInto for IMapView +{ +} +impl + windows_core::imp::CanInto for IMapView +{ +} +unsafe impl + windows_core::Interface for IMapView +{ + type Vtable = IMapView_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl + windows_core::RuntimeType for IMapView +{ + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({e480ce40-a338-4ada-adcf-272272e48cb9}") + .push_slice(b";") + .push_other(K::SIGNATURE) + .push_slice(b";") + .push_other(V::SIGNATURE) + .push_slice(b")"); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"Windows.Foundation.Collections.IMapView`2<") + .push_other(K::NAME) + .push_slice(b", ") + .push_other(V::NAME) + .push_slice(b">"); +} +impl + windows_core::imp::CanInto>> for IMapView +{ + const QUERY: bool = true; +} +impl + IMapView +{ + pub fn Lookup(&self, key: P0) -> windows_core::Result + where + P0: windows_core::Param, + { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Lookup)( + windows_core::Interface::as_raw(self), + key.param().abi(), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn Size(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Size)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .map(|| result__) + } + } + pub fn HasKey(&self, key: P0) -> windows_core::Result + where + P0: windows_core::Param, + { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).HasKey)( + windows_core::Interface::as_raw(self), + key.param().abi(), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Split( + &self, + first: &mut Option>, + second: &mut Option>, + ) -> windows_core::Result<()> { + unsafe { + (windows_core::Interface::vtable(self).Split)( + windows_core::Interface::as_raw(self), + first as *mut _ as _, + second as *mut _ as _, + ) + .ok() + } + } + pub fn First(&self) -> windows_core::Result>> { + let this = &windows_core::Interface::cast::>>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).First)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl IntoIterator + for IMapView +{ + type Item = IKeyValuePair; + type IntoIter = IIterator; + fn into_iter(self) -> Self::IntoIter { + IntoIterator::into_iter(&self) + } +} +impl IntoIterator + for &IMapView +{ + type Item = IKeyValuePair; + type IntoIter = IIterator; + fn into_iter(self) -> Self::IntoIter { + self.First().unwrap() + } +} +impl + windows_core::RuntimeName for IMapView +{ + const NAME: &'static str = "Windows.Foundation.Collections.IMapView"; + const RUNTIME_CLASS_NAME: windows_core::imp::ConstBuffer = + ::NAME; +} +pub trait IMapView_Impl: IIterable_Impl> +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, +{ + fn Lookup(&self, key: windows_core::Ref) -> windows_core::Result; + fn Size(&self) -> windows_core::Result; + fn HasKey(&self, key: windows_core::Ref) -> windows_core::Result; + fn Split( + &self, + first: windows_core::OutRef>, + second: windows_core::OutRef>, + ) -> windows_core::Result<()>; +} +impl + IMapView_Vtbl +{ + pub const fn new, const OFFSET: isize>() -> Self { + unsafe extern "system" fn Lookup< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IMapView_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + key: windows_core::AbiType, + result__: *mut windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IMapView_Impl::Lookup(this, core::mem::transmute_copy(&key)) { + 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 Size< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IMapView_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut u32, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IMapView_Impl::Size(this) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn HasKey< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IMapView_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + key: windows_core::AbiType, + result__: *mut bool, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IMapView_Impl::HasKey(this, core::mem::transmute_copy(&key)) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn Split< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IMapView_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + first: *mut *mut core::ffi::c_void, + second: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IMapView_Impl::Split( + this, + core::mem::transmute_copy(&first), + core::mem::transmute_copy(&second), + ) + .into() + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::, OFFSET>(), + Lookup: Lookup::, + Size: Size::, + HasKey: HasKey::, + Split: Split::, + K: core::marker::PhantomData::, + V: core::marker::PhantomData::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == & as windows_core::Interface>::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IMapView_Vtbl +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, +{ + pub base__: windows_core::IInspectable_Vtbl, + pub Lookup: unsafe extern "system" fn( + *mut core::ffi::c_void, + windows_core::AbiType, + *mut windows_core::AbiType, + ) -> windows_core::HRESULT, + pub Size: unsafe extern "system" fn(*mut core::ffi::c_void, *mut u32) -> windows_core::HRESULT, + pub HasKey: unsafe extern "system" fn( + *mut core::ffi::c_void, + windows_core::AbiType, + *mut bool, + ) -> windows_core::HRESULT, + pub Split: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + K: core::marker::PhantomData, + V: core::marker::PhantomData, +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IObservableMap( + windows_core::IUnknown, + core::marker::PhantomData, + core::marker::PhantomData, +) +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static; +impl + windows_core::imp::CanInto for IObservableMap +{ +} +impl + windows_core::imp::CanInto for IObservableMap +{ +} +unsafe impl + windows_core::Interface for IObservableMap +{ + type Vtable = IObservableMap_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl + windows_core::RuntimeType for IObservableMap +{ + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({65df2bf5-bf39-41b5-aebc-5a9d865e472b}") + .push_slice(b";") + .push_other(K::SIGNATURE) + .push_slice(b";") + .push_other(V::SIGNATURE) + .push_slice(b")"); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"Windows.Foundation.Collections.IObservableMap`2<") + .push_other(K::NAME) + .push_slice(b", ") + .push_other(V::NAME) + .push_slice(b">"); +} +impl + windows_core::imp::CanInto>> for IObservableMap +{ + const QUERY: bool = true; +} +impl + windows_core::imp::CanInto> for IObservableMap +{ + const QUERY: bool = true; +} +impl + IObservableMap +{ + pub fn MapChanged(&self, vhnd: P0) -> windows_core::Result + where + P0: windows_core::Param>, + { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).MapChanged)( + windows_core::Interface::as_raw(self), + vhnd.param().abi(), + &mut result__, + ) + .map(|| result__) + } + } + pub fn RemoveMapChanged(&self, token: i64) -> windows_core::Result<()> { + unsafe { + (windows_core::Interface::vtable(self).RemoveMapChanged)( + windows_core::Interface::as_raw(self), + token, + ) + .ok() + } + } + pub fn First(&self) -> windows_core::Result>> { + let this = &windows_core::Interface::cast::>>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).First)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn Lookup(&self, key: P0) -> windows_core::Result + where + P0: windows_core::Param, + { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Lookup)( + windows_core::Interface::as_raw(this), + key.param().abi(), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn Size(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Size)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn HasKey(&self, key: P0) -> windows_core::Result + where + P0: windows_core::Param, + { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).HasKey)( + windows_core::Interface::as_raw(this), + key.param().abi(), + &mut result__, + ) + .map(|| result__) + } + } + pub fn GetView(&self) -> windows_core::Result> { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).GetView)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn Insert(&self, key: P0, value: P1) -> windows_core::Result + where + P0: windows_core::Param, + P1: windows_core::Param, + { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Insert)( + windows_core::Interface::as_raw(this), + key.param().abi(), + value.param().abi(), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Remove(&self, key: P0) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + (windows_core::Interface::vtable(this).Remove)( + windows_core::Interface::as_raw(this), + key.param().abi(), + ) + .ok() + } + } + pub fn Clear(&self) -> windows_core::Result<()> { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + (windows_core::Interface::vtable(this).Clear)(windows_core::Interface::as_raw(this)) + .ok() + } + } +} +impl IntoIterator + for IObservableMap +{ + type Item = IKeyValuePair; + type IntoIter = IIterator; + fn into_iter(self) -> Self::IntoIter { + IntoIterator::into_iter(&self) + } +} +impl IntoIterator + for &IObservableMap +{ + type Item = IKeyValuePair; + type IntoIter = IIterator; + fn into_iter(self) -> Self::IntoIter { + self.First().unwrap() + } +} +impl + windows_core::RuntimeName for IObservableMap +{ + const NAME: &'static str = "Windows.Foundation.Collections.IObservableMap"; + const RUNTIME_CLASS_NAME: windows_core::imp::ConstBuffer = + ::NAME; +} +pub trait IObservableMap_Impl: IIterable_Impl> + IMap_Impl +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, +{ + fn MapChanged( + &self, + vhnd: windows_core::Ref>, + ) -> windows_core::Result; + fn RemoveMapChanged(&self, token: i64) -> windows_core::Result<()>; +} +impl + IObservableMap_Vtbl +{ + pub const fn new, const OFFSET: isize>() -> Self { + unsafe extern "system" fn MapChanged< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IObservableMap_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + vhnd: *mut core::ffi::c_void, + result__: *mut i64, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IObservableMap_Impl::MapChanged(this, core::mem::transmute_copy(&vhnd)) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn RemoveMapChanged< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + Identity: IObservableMap_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + token: i64, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IObservableMap_Impl::RemoveMapChanged(this, token).into() + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::, OFFSET>( + ), + MapChanged: MapChanged::, + RemoveMapChanged: RemoveMapChanged::, + K: core::marker::PhantomData::, + V: core::marker::PhantomData::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == & as windows_core::Interface>::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IObservableMap_Vtbl +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, +{ + pub base__: windows_core::IInspectable_Vtbl, + pub MapChanged: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut i64, + ) -> windows_core::HRESULT, + pub RemoveMapChanged: + unsafe extern "system" fn(*mut core::ffi::c_void, i64) -> windows_core::HRESULT, + K: core::marker::PhantomData, + V: core::marker::PhantomData, +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IObservableVector(windows_core::IUnknown, core::marker::PhantomData) +where + T: windows_core::RuntimeType + 'static; +impl windows_core::imp::CanInto + for IObservableVector +{ +} +impl windows_core::imp::CanInto + for IObservableVector +{ +} +unsafe impl windows_core::Interface + for IObservableVector +{ + type Vtable = IObservableVector_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl windows_core::RuntimeType for IObservableVector { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({5917eb53-50b4-4a0d-b309-65862b3f1dbc}") + .push_slice(b";") + .push_other(T::SIGNATURE) + .push_slice(b")"); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"Windows.Foundation.Collections.IObservableVector`1<") + .push_other(T::NAME) + .push_slice(b">"); +} +impl windows_core::imp::CanInto> + for IObservableVector +{ + const QUERY: bool = true; +} +impl windows_core::imp::CanInto> + for IObservableVector +{ + const QUERY: bool = true; +} +impl IObservableVector { + pub fn VectorChanged(&self, vhnd: P0) -> windows_core::Result + where + P0: windows_core::Param>, + { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).VectorChanged)( + windows_core::Interface::as_raw(self), + vhnd.param().abi(), + &mut result__, + ) + .map(|| result__) + } + } + pub fn RemoveVectorChanged(&self, token: i64) -> windows_core::Result<()> { + unsafe { + (windows_core::Interface::vtable(self).RemoveVectorChanged)( + windows_core::Interface::as_raw(self), + token, + ) + .ok() + } + } + pub fn First(&self) -> windows_core::Result> { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).First)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn GetAt(&self, index: u32) -> windows_core::Result { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).GetAt)( + windows_core::Interface::as_raw(this), + index, + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn Size(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Size)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn GetView(&self) -> windows_core::Result> { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).GetView)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn IndexOf(&self, value: P0, index: &mut u32) -> windows_core::Result + where + P0: windows_core::Param, + { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).IndexOf)( + windows_core::Interface::as_raw(this), + value.param().abi(), + index, + &mut result__, + ) + .map(|| result__) + } + } + pub fn SetAt(&self, index: u32, value: P1) -> windows_core::Result<()> + where + P1: windows_core::Param, + { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + (windows_core::Interface::vtable(this).SetAt)( + windows_core::Interface::as_raw(this), + index, + value.param().abi(), + ) + .ok() + } + } + pub fn InsertAt(&self, index: u32, value: P1) -> windows_core::Result<()> + where + P1: windows_core::Param, + { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + (windows_core::Interface::vtable(this).InsertAt)( + windows_core::Interface::as_raw(this), + index, + value.param().abi(), + ) + .ok() + } + } + pub fn RemoveAt(&self, index: u32) -> windows_core::Result<()> { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + (windows_core::Interface::vtable(this).RemoveAt)( + windows_core::Interface::as_raw(this), + index, + ) + .ok() + } + } + pub fn Append(&self, value: P0) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + (windows_core::Interface::vtable(this).Append)( + windows_core::Interface::as_raw(this), + value.param().abi(), + ) + .ok() + } + } + pub fn RemoveAtEnd(&self) -> windows_core::Result<()> { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + (windows_core::Interface::vtable(this).RemoveAtEnd)(windows_core::Interface::as_raw( + this, + )) + .ok() + } + } + pub fn Clear(&self) -> windows_core::Result<()> { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + (windows_core::Interface::vtable(this).Clear)(windows_core::Interface::as_raw(this)) + .ok() + } + } + pub fn GetMany( + &self, + startindex: u32, + items: &mut [>::Default], + ) -> windows_core::Result { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).GetMany)( + windows_core::Interface::as_raw(this), + startindex, + items.len().try_into().unwrap(), + core::mem::transmute_copy(&items), + &mut result__, + ) + .map(|| result__) + } + } + pub fn ReplaceAll( + &self, + items: &[>::Default], + ) -> windows_core::Result<()> { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + (windows_core::Interface::vtable(this).ReplaceAll)( + windows_core::Interface::as_raw(this), + items.len().try_into().unwrap(), + core::mem::transmute(items.as_ptr()), + ) + .ok() + } + } +} +impl IntoIterator for IObservableVector { + type Item = T; + type IntoIter = IIterator; + fn into_iter(self) -> Self::IntoIter { + IntoIterator::into_iter(&self) + } +} +impl IntoIterator for &IObservableVector { + type Item = T; + type IntoIter = IIterator; + fn into_iter(self) -> Self::IntoIter { + self.First().unwrap() + } +} +impl windows_core::RuntimeName for IObservableVector { + const NAME: &'static str = "Windows.Foundation.Collections.IObservableVector"; + const RUNTIME_CLASS_NAME: windows_core::imp::ConstBuffer = + ::NAME; +} +pub trait IObservableVector_Impl: IIterable_Impl + IVector_Impl +where + T: windows_core::RuntimeType + 'static, +{ + fn VectorChanged( + &self, + vhnd: windows_core::Ref>, + ) -> windows_core::Result; + fn RemoveVectorChanged(&self, token: i64) -> windows_core::Result<()>; +} +impl IObservableVector_Vtbl { + pub const fn new, const OFFSET: isize>() -> Self { + unsafe extern "system" fn VectorChanged< + T: windows_core::RuntimeType + 'static, + Identity: IObservableVector_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + vhnd: *mut core::ffi::c_void, + result__: *mut i64, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IObservableVector_Impl::VectorChanged(this, core::mem::transmute_copy(&vhnd)) + { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn RemoveVectorChanged< + T: windows_core::RuntimeType + 'static, + Identity: IObservableVector_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + token: i64, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IObservableVector_Impl::RemoveVectorChanged(this, token).into() + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::, OFFSET>( + ), + VectorChanged: VectorChanged::, + RemoveVectorChanged: RemoveVectorChanged::, + T: core::marker::PhantomData::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == & as windows_core::Interface>::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IObservableVector_Vtbl +where + T: windows_core::RuntimeType + 'static, +{ + pub base__: windows_core::IInspectable_Vtbl, + pub VectorChanged: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + *mut i64, + ) -> windows_core::HRESULT, + pub RemoveVectorChanged: + unsafe extern "system" fn(*mut core::ffi::c_void, i64) -> windows_core::HRESULT, + T: core::marker::PhantomData, +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IVector(windows_core::IUnknown, core::marker::PhantomData) +where + T: windows_core::RuntimeType + 'static; +impl windows_core::imp::CanInto + for IVector +{ +} +impl windows_core::imp::CanInto + for IVector +{ +} +unsafe impl windows_core::Interface for IVector { + type Vtable = IVector_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl windows_core::RuntimeType for IVector { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({913337e9-11a1-4345-a3a2-4e7f956e222d}") + .push_slice(b";") + .push_other(T::SIGNATURE) + .push_slice(b")"); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"Windows.Foundation.Collections.IVector`1<") + .push_other(T::NAME) + .push_slice(b">"); +} +impl windows_core::imp::CanInto> + for IVector +{ + const QUERY: bool = true; +} +impl IVector { + pub fn GetAt(&self, index: u32) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).GetAt)( + windows_core::Interface::as_raw(self), + index, + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn Size(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Size)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .map(|| result__) + } + } + pub fn GetView(&self) -> windows_core::Result> { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).GetView)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn IndexOf(&self, value: P0, index: &mut u32) -> windows_core::Result + where + P0: windows_core::Param, + { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).IndexOf)( + windows_core::Interface::as_raw(self), + value.param().abi(), + index, + &mut result__, + ) + .map(|| result__) + } + } + pub fn SetAt(&self, index: u32, value: P1) -> windows_core::Result<()> + where + P1: windows_core::Param, + { + unsafe { + (windows_core::Interface::vtable(self).SetAt)( + windows_core::Interface::as_raw(self), + index, + value.param().abi(), + ) + .ok() + } + } + pub fn InsertAt(&self, index: u32, value: P1) -> windows_core::Result<()> + where + P1: windows_core::Param, + { + unsafe { + (windows_core::Interface::vtable(self).InsertAt)( + windows_core::Interface::as_raw(self), + index, + value.param().abi(), + ) + .ok() + } + } + pub fn RemoveAt(&self, index: u32) -> windows_core::Result<()> { + unsafe { + (windows_core::Interface::vtable(self).RemoveAt)( + windows_core::Interface::as_raw(self), + index, + ) + .ok() + } + } + pub fn Append(&self, value: P0) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + unsafe { + (windows_core::Interface::vtable(self).Append)( + windows_core::Interface::as_raw(self), + value.param().abi(), + ) + .ok() + } + } + pub fn RemoveAtEnd(&self) -> windows_core::Result<()> { + unsafe { + (windows_core::Interface::vtable(self).RemoveAtEnd)(windows_core::Interface::as_raw( + self, + )) + .ok() + } + } + pub fn Clear(&self) -> windows_core::Result<()> { + unsafe { + (windows_core::Interface::vtable(self).Clear)(windows_core::Interface::as_raw(self)) + .ok() + } + } + pub fn GetMany( + &self, + startindex: u32, + items: &mut [>::Default], + ) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).GetMany)( + windows_core::Interface::as_raw(self), + startindex, + items.len().try_into().unwrap(), + core::mem::transmute_copy(&items), + &mut result__, + ) + .map(|| result__) + } + } + pub fn ReplaceAll( + &self, + items: &[>::Default], + ) -> windows_core::Result<()> { + unsafe { + (windows_core::Interface::vtable(self).ReplaceAll)( + windows_core::Interface::as_raw(self), + items.len().try_into().unwrap(), + core::mem::transmute(items.as_ptr()), + ) + .ok() + } + } + pub fn First(&self) -> windows_core::Result> { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).First)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl IntoIterator for IVector { + type Item = T; + type IntoIter = IIterator; + fn into_iter(self) -> Self::IntoIter { + IntoIterator::into_iter(&self) + } +} +impl IntoIterator for &IVector { + type Item = T; + type IntoIter = IIterator; + fn into_iter(self) -> Self::IntoIter { + self.First().unwrap() + } +} +impl windows_core::RuntimeName for IVector { + const NAME: &'static str = "Windows.Foundation.Collections.IVector"; + const RUNTIME_CLASS_NAME: windows_core::imp::ConstBuffer = + ::NAME; +} +pub trait IVector_Impl: IIterable_Impl +where + T: windows_core::RuntimeType + 'static, +{ + fn GetAt(&self, index: u32) -> windows_core::Result; + fn Size(&self) -> windows_core::Result; + fn GetView(&self) -> windows_core::Result>; + fn IndexOf(&self, value: windows_core::Ref, index: &mut u32) -> windows_core::Result; + fn SetAt(&self, index: u32, value: windows_core::Ref) -> windows_core::Result<()>; + fn InsertAt(&self, index: u32, value: windows_core::Ref) -> windows_core::Result<()>; + fn RemoveAt(&self, index: u32) -> windows_core::Result<()>; + fn Append(&self, value: windows_core::Ref) -> windows_core::Result<()>; + fn RemoveAtEnd(&self) -> windows_core::Result<()>; + fn Clear(&self) -> windows_core::Result<()>; + fn GetMany( + &self, + startIndex: u32, + items: &mut [>::Default], + ) -> windows_core::Result; + fn ReplaceAll( + &self, + items: &[>::Default], + ) -> windows_core::Result<()>; +} +impl IVector_Vtbl { + pub const fn new, const OFFSET: isize>() -> Self { + unsafe extern "system" fn GetAt< + T: windows_core::RuntimeType + 'static, + Identity: IVector_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + index: u32, + result__: *mut windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IVector_Impl::GetAt(this, index) { + 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 Size< + T: windows_core::RuntimeType + 'static, + Identity: IVector_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut u32, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IVector_Impl::Size(this) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn GetView< + T: windows_core::RuntimeType + 'static, + Identity: IVector_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IVector_Impl::GetView(this) { + 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 IndexOf< + T: windows_core::RuntimeType + 'static, + Identity: IVector_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + value: windows_core::AbiType, + index: *mut u32, + result__: *mut bool, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IVector_Impl::IndexOf( + this, + core::mem::transmute_copy(&value), + core::mem::transmute_copy(&index), + ) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn SetAt< + T: windows_core::RuntimeType + 'static, + Identity: IVector_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + index: u32, + value: windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IVector_Impl::SetAt(this, index, core::mem::transmute_copy(&value)).into() + } + } + unsafe extern "system" fn InsertAt< + T: windows_core::RuntimeType + 'static, + Identity: IVector_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + index: u32, + value: windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IVector_Impl::InsertAt(this, index, core::mem::transmute_copy(&value)).into() + } + } + unsafe extern "system" fn RemoveAt< + T: windows_core::RuntimeType + 'static, + Identity: IVector_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + index: u32, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IVector_Impl::RemoveAt(this, index).into() + } + } + unsafe extern "system" fn Append< + T: windows_core::RuntimeType + 'static, + Identity: IVector_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + value: windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IVector_Impl::Append(this, core::mem::transmute_copy(&value)).into() + } + } + unsafe extern "system" fn RemoveAtEnd< + T: windows_core::RuntimeType + 'static, + Identity: IVector_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IVector_Impl::RemoveAtEnd(this).into() + } + } + unsafe extern "system" fn Clear< + T: windows_core::RuntimeType + 'static, + Identity: IVector_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IVector_Impl::Clear(this).into() + } + } + unsafe extern "system" fn GetMany< + T: windows_core::RuntimeType + 'static, + Identity: IVector_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + startindex: u32, + items_array_size: u32, + items: *mut windows_core::AbiType, + result__: *mut u32, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IVector_Impl::GetMany( + this, + startindex, + core::slice::from_raw_parts_mut( + core::mem::transmute_copy(&items), + items_array_size as usize, + ), + ) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn ReplaceAll< + T: windows_core::RuntimeType + 'static, + Identity: IVector_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + items_array_size: u32, + items: *const windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IVector_Impl::ReplaceAll( + this, + core::slice::from_raw_parts( + core::mem::transmute_copy(&items), + items_array_size as usize, + ), + ) + .into() + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::, OFFSET>(), + GetAt: GetAt::, + Size: Size::, + GetView: GetView::, + IndexOf: IndexOf::, + SetAt: SetAt::, + InsertAt: InsertAt::, + RemoveAt: RemoveAt::, + Append: Append::, + RemoveAtEnd: RemoveAtEnd::, + Clear: Clear::, + GetMany: GetMany::, + ReplaceAll: ReplaceAll::, + T: core::marker::PhantomData::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == & as windows_core::Interface>::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IVector_Vtbl +where + T: windows_core::RuntimeType + 'static, +{ + pub base__: windows_core::IInspectable_Vtbl, + pub GetAt: unsafe extern "system" fn( + *mut core::ffi::c_void, + u32, + *mut windows_core::AbiType, + ) -> windows_core::HRESULT, + pub Size: unsafe extern "system" fn(*mut core::ffi::c_void, *mut u32) -> windows_core::HRESULT, + pub GetView: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub IndexOf: unsafe extern "system" fn( + *mut core::ffi::c_void, + windows_core::AbiType, + *mut u32, + *mut bool, + ) -> windows_core::HRESULT, + pub SetAt: unsafe extern "system" fn( + *mut core::ffi::c_void, + u32, + windows_core::AbiType, + ) -> windows_core::HRESULT, + pub InsertAt: unsafe extern "system" fn( + *mut core::ffi::c_void, + u32, + windows_core::AbiType, + ) -> windows_core::HRESULT, + pub RemoveAt: unsafe extern "system" fn(*mut core::ffi::c_void, u32) -> windows_core::HRESULT, + pub Append: unsafe extern "system" fn( + *mut core::ffi::c_void, + windows_core::AbiType, + ) -> windows_core::HRESULT, + pub RemoveAtEnd: unsafe extern "system" fn(*mut core::ffi::c_void) -> windows_core::HRESULT, + pub Clear: unsafe extern "system" fn(*mut core::ffi::c_void) -> windows_core::HRESULT, + pub GetMany: unsafe extern "system" fn( + *mut core::ffi::c_void, + u32, + u32, + *mut windows_core::AbiType, + *mut u32, + ) -> windows_core::HRESULT, + pub ReplaceAll: unsafe extern "system" fn( + *mut core::ffi::c_void, + u32, + *const windows_core::AbiType, + ) -> windows_core::HRESULT, + T: core::marker::PhantomData, +} +windows_core::imp::define_interface!( + IVectorChangedEventArgs, + IVectorChangedEventArgs_Vtbl, + 0x575933df_34fe_4480_af15_07691f3d5d9b +); +impl windows_core::RuntimeType for IVectorChangedEventArgs { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::from_slice( + b"Windows.Foundation.Collections.IVectorChangedEventArgs", + ); +} +windows_core::imp::interface_hierarchy!( + IVectorChangedEventArgs, + windows_core::IUnknown, + windows_core::IInspectable +); +impl IVectorChangedEventArgs { + pub fn CollectionChange(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).CollectionChange)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Index(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Index)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .map(|| result__) + } + } +} +impl windows_core::RuntimeName for IVectorChangedEventArgs { + const NAME: &'static str = "Windows.Foundation.Collections.IVectorChangedEventArgs"; +} +pub trait IVectorChangedEventArgs_Impl: windows_core::IUnknownImpl { + fn CollectionChange(&self) -> windows_core::Result; + fn Index(&self) -> windows_core::Result; +} +impl IVectorChangedEventArgs_Vtbl { + pub const fn new() -> Self { + unsafe extern "system" fn CollectionChange< + Identity: IVectorChangedEventArgs_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut CollectionChange, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IVectorChangedEventArgs_Impl::CollectionChange(this) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn Index< + Identity: IVectorChangedEventArgs_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut u32, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IVectorChangedEventArgs_Impl::Index(this) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::( + ), + CollectionChange: CollectionChange::, + Index: Index::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IVectorChangedEventArgs_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub CollectionChange: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut CollectionChange, + ) -> windows_core::HRESULT, + pub Index: unsafe extern "system" fn(*mut core::ffi::c_void, *mut u32) -> windows_core::HRESULT, +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IVectorView(windows_core::IUnknown, core::marker::PhantomData) +where + T: windows_core::RuntimeType + 'static; +impl windows_core::imp::CanInto + for IVectorView +{ +} +impl windows_core::imp::CanInto + for IVectorView +{ +} +unsafe impl windows_core::Interface for IVectorView { + type Vtable = IVectorView_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl windows_core::RuntimeType for IVectorView { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({bbe1fa4c-b0e3-4583-baef-1f1b2e483e56}") + .push_slice(b";") + .push_other(T::SIGNATURE) + .push_slice(b")"); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"Windows.Foundation.Collections.IVectorView`1<") + .push_other(T::NAME) + .push_slice(b">"); +} +impl windows_core::imp::CanInto> + for IVectorView +{ + const QUERY: bool = true; +} +impl IVectorView { + pub fn GetAt(&self, index: u32) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).GetAt)( + windows_core::Interface::as_raw(self), + index, + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn Size(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Size)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .map(|| result__) + } + } + pub fn IndexOf(&self, value: P0, index: &mut u32) -> windows_core::Result + where + P0: windows_core::Param, + { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).IndexOf)( + windows_core::Interface::as_raw(self), + value.param().abi(), + index, + &mut result__, + ) + .map(|| result__) + } + } + pub fn GetMany( + &self, + startindex: u32, + items: &mut [>::Default], + ) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).GetMany)( + windows_core::Interface::as_raw(self), + startindex, + items.len().try_into().unwrap(), + core::mem::transmute_copy(&items), + &mut result__, + ) + .map(|| result__) + } + } + pub fn First(&self) -> windows_core::Result> { + let this = &windows_core::Interface::cast::>(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).First)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +impl IntoIterator for IVectorView { + type Item = T; + type IntoIter = IIterator; + fn into_iter(self) -> Self::IntoIter { + IntoIterator::into_iter(&self) + } +} +impl IntoIterator for &IVectorView { + type Item = T; + type IntoIter = IIterator; + fn into_iter(self) -> Self::IntoIter { + self.First().unwrap() + } +} +impl windows_core::RuntimeName for IVectorView { + const NAME: &'static str = "Windows.Foundation.Collections.IVectorView"; + const RUNTIME_CLASS_NAME: windows_core::imp::ConstBuffer = + ::NAME; +} +pub trait IVectorView_Impl: IIterable_Impl +where + T: windows_core::RuntimeType + 'static, +{ + fn GetAt(&self, index: u32) -> windows_core::Result; + fn Size(&self) -> windows_core::Result; + fn IndexOf(&self, value: windows_core::Ref, index: &mut u32) -> windows_core::Result; + fn GetMany( + &self, + startIndex: u32, + items: &mut [>::Default], + ) -> windows_core::Result; +} +impl IVectorView_Vtbl { + pub const fn new, const OFFSET: isize>() -> Self { + unsafe extern "system" fn GetAt< + T: windows_core::RuntimeType + 'static, + Identity: IVectorView_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + index: u32, + result__: *mut windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IVectorView_Impl::GetAt(this, index) { + 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 Size< + T: windows_core::RuntimeType + 'static, + Identity: IVectorView_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut u32, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IVectorView_Impl::Size(this) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn IndexOf< + T: windows_core::RuntimeType + 'static, + Identity: IVectorView_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + value: windows_core::AbiType, + index: *mut u32, + result__: *mut bool, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IVectorView_Impl::IndexOf( + this, + core::mem::transmute_copy(&value), + core::mem::transmute_copy(&index), + ) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn GetMany< + T: windows_core::RuntimeType + 'static, + Identity: IVectorView_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + startindex: u32, + items_array_size: u32, + items: *mut windows_core::AbiType, + result__: *mut u32, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IVectorView_Impl::GetMany( + this, + startindex, + core::slice::from_raw_parts_mut( + core::mem::transmute_copy(&items), + items_array_size as usize, + ), + ) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::, OFFSET>(), + GetAt: GetAt::, + Size: Size::, + IndexOf: IndexOf::, + GetMany: GetMany::, + T: core::marker::PhantomData::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == & as windows_core::Interface>::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IVectorView_Vtbl +where + T: windows_core::RuntimeType + 'static, +{ + pub base__: windows_core::IInspectable_Vtbl, + pub GetAt: unsafe extern "system" fn( + *mut core::ffi::c_void, + u32, + *mut windows_core::AbiType, + ) -> windows_core::HRESULT, + pub Size: unsafe extern "system" fn(*mut core::ffi::c_void, *mut u32) -> windows_core::HRESULT, + pub IndexOf: unsafe extern "system" fn( + *mut core::ffi::c_void, + windows_core::AbiType, + *mut u32, + *mut bool, + ) -> windows_core::HRESULT, + pub GetMany: unsafe extern "system" fn( + *mut core::ffi::c_void, + u32, + u32, + *mut windows_core::AbiType, + *mut u32, + ) -> windows_core::HRESULT, + T: core::marker::PhantomData, +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct MapChangedEventHandler( + windows_core::IUnknown, + core::marker::PhantomData, + core::marker::PhantomData, +) +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static; +unsafe impl + windows_core::Interface for MapChangedEventHandler +{ + type Vtable = MapChangedEventHandler_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl + windows_core::RuntimeType for MapChangedEventHandler +{ + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({179517f3-94ee-41f8-bddc-768a895544f3}") + .push_slice(b";") + .push_other(K::SIGNATURE) + .push_slice(b";") + .push_other(V::SIGNATURE) + .push_slice(b")"); +} +impl + MapChangedEventHandler +{ + pub fn new< + F: Fn( + windows_core::Ref>, + windows_core::Ref>, + ) -> windows_core::Result<()> + + Send + + 'static, + >( + invoke: F, + ) -> Self { + let com = windows_core::imp::DelegateBox::, F>::new( + &MapChangedEventHandlerBox::::VTABLE, + invoke, + ); + unsafe { core::mem::transmute(windows_core::imp::Box::new(com)) } + } + pub fn Invoke(&self, sender: P0, event: P1) -> windows_core::Result<()> + where + P0: windows_core::Param>, + P1: windows_core::Param>, + { + unsafe { + (windows_core::Interface::vtable(self).Invoke)( + windows_core::Interface::as_raw(self), + sender.param().abi(), + event.param().abi(), + ) + .ok() + } + } +} +#[repr(C)] +#[doc(hidden)] +pub struct MapChangedEventHandler_Vtbl +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, +{ + base__: windows_core::IUnknown_Vtbl, + Invoke: unsafe extern "system" fn( + this: *mut core::ffi::c_void, + sender: *mut core::ffi::c_void, + event: *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + K: core::marker::PhantomData, + V: core::marker::PhantomData, +} +struct MapChangedEventHandlerBox< + K, + V, + F: Fn( + windows_core::Ref>, + windows_core::Ref>, + ) -> windows_core::Result<()> + + Send + + 'static, +>(core::marker::PhantomData<(K, V, fn() -> F)>) +where + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static; +impl< + K: windows_core::RuntimeType + 'static, + V: windows_core::RuntimeType + 'static, + F: Fn( + windows_core::Ref>, + windows_core::Ref>, + ) -> windows_core::Result<()> + + Send + + 'static, + > MapChangedEventHandlerBox +{ + const VTABLE: MapChangedEventHandler_Vtbl = MapChangedEventHandler_Vtbl:: { + base__: windows_core::IUnknown_Vtbl { + QueryInterface: + windows_core::imp::DelegateBox::, F>::QueryInterface, + AddRef: windows_core::imp::DelegateBox::, F>::AddRef, + Release: windows_core::imp::DelegateBox::, F>::Release, + }, + Invoke: Self::Invoke, + K: core::marker::PhantomData::, + V: core::marker::PhantomData::, + }; + unsafe extern "system" fn Invoke( + this: *mut core::ffi::c_void, + sender: *mut core::ffi::c_void, + event: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this = &mut *(this as *mut *mut core::ffi::c_void + as *mut windows_core::imp::DelegateBox, F>); + (this.invoke)( + core::mem::transmute_copy(&sender), + core::mem::transmute_copy(&event), + ) + .into() + } + } +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct VectorChangedEventHandler(windows_core::IUnknown, core::marker::PhantomData) +where + T: windows_core::RuntimeType + 'static; +unsafe impl windows_core::Interface + for VectorChangedEventHandler +{ + type Vtable = VectorChangedEventHandler_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl windows_core::RuntimeType + for VectorChangedEventHandler +{ + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({0c051752-9fbf-4c70-aa0c-0e4c82d9a761}") + .push_slice(b";") + .push_other(T::SIGNATURE) + .push_slice(b")"); +} +impl VectorChangedEventHandler { + pub fn new< + F: Fn( + windows_core::Ref>, + windows_core::Ref, + ) -> windows_core::Result<()> + + Send + + 'static, + >( + invoke: F, + ) -> Self { + let com = windows_core::imp::DelegateBox::, F>::new( + &VectorChangedEventHandlerBox::::VTABLE, + invoke, + ); + unsafe { core::mem::transmute(windows_core::imp::Box::new(com)) } + } + pub fn Invoke(&self, sender: P0, event: P1) -> windows_core::Result<()> + where + P0: windows_core::Param>, + P1: windows_core::Param, + { + unsafe { + (windows_core::Interface::vtable(self).Invoke)( + windows_core::Interface::as_raw(self), + sender.param().abi(), + event.param().abi(), + ) + .ok() + } + } +} +#[repr(C)] +#[doc(hidden)] +pub struct VectorChangedEventHandler_Vtbl +where + T: windows_core::RuntimeType + 'static, +{ + base__: windows_core::IUnknown_Vtbl, + Invoke: unsafe extern "system" fn( + this: *mut core::ffi::c_void, + sender: *mut core::ffi::c_void, + event: *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + T: core::marker::PhantomData, +} +struct VectorChangedEventHandlerBox< + T, + F: Fn( + windows_core::Ref>, + windows_core::Ref, + ) -> windows_core::Result<()> + + Send + + 'static, +>(core::marker::PhantomData<(T, fn() -> F)>) +where + T: windows_core::RuntimeType + 'static; +impl< + T: windows_core::RuntimeType + 'static, + F: Fn( + windows_core::Ref>, + windows_core::Ref, + ) -> windows_core::Result<()> + + Send + + 'static, + > VectorChangedEventHandlerBox +{ + const VTABLE: VectorChangedEventHandler_Vtbl = VectorChangedEventHandler_Vtbl:: { + base__: windows_core::IUnknown_Vtbl { + QueryInterface: + windows_core::imp::DelegateBox::, F>::QueryInterface, + AddRef: windows_core::imp::DelegateBox::, F>::AddRef, + Release: windows_core::imp::DelegateBox::, F>::Release, + }, + Invoke: Self::Invoke, + T: core::marker::PhantomData::, + }; + unsafe extern "system" fn Invoke( + this: *mut core::ffi::c_void, + sender: *mut core::ffi::c_void, + event: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this = &mut *(this as *mut *mut core::ffi::c_void + as *mut windows_core::imp::DelegateBox, F>); + (this.invoke)( + core::mem::transmute_copy(&sender), + core::mem::transmute_copy(&event), + ) + .into() + } + } +} diff --git a/third_party/windows-winui/windows-collections/src/iterable.rs b/third_party/windows-winui/windows-collections/src/iterable.rs new file mode 100644 index 0000000000..993b6fee57 --- /dev/null +++ b/third_party/windows-winui/windows-collections/src/iterable.rs @@ -0,0 +1,111 @@ +use super::*; +use windows_core::*; + +struct StockIterable +where + T: RuntimeType + 'static, + T::Default: Clone, +{ + values: Vec, +} + +implement_decl! { + impl StockIterable as StockIterable_Impl: [ + IIterable, + ] + where T: RuntimeType + 'static, T::Default: Clone +} + +impl IIterable_Impl for StockIterable_Impl +where + T: RuntimeType, + T::Default: Clone, +{ + fn First(&self) -> Result> { + Ok(ComObject::new(StockIterator { + owner: self.to_object(), + current: 0.into(), + }) + .into_interface()) + } +} + +struct StockIterator +where + T: RuntimeType + 'static, + T::Default: Clone, +{ + owner: ComObject>, + current: std::sync::atomic::AtomicUsize, +} + +implement_decl! { + impl StockIterator as StockIterator_Impl: [ + IIterator, + ] + where T: RuntimeType + 'static, T::Default: Clone +} + +impl IIterator_Impl for StockIterator_Impl +where + T: RuntimeType, + T::Default: Clone, +{ + fn Current(&self) -> Result { + let owner: &StockIterable = &self.owner; + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + + if self.owner.values.len() > current { + T::from_default(&owner.values[current]) + } else { + Err(Error::from(E_BOUNDS)) + } + } + + fn HasCurrent(&self) -> Result { + let owner: &StockIterable = &self.owner; + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + + Ok(owner.values.len() > current) + } + + fn MoveNext(&self) -> Result { + let owner: &StockIterable = &self.owner; + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + + if current < owner.values.len() { + self.current + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + } + + Ok(owner.values.len() > current + 1) + } + + fn GetMany(&self, values: &mut [T::Default]) -> Result { + let owner: &StockIterable = &self.owner; + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + + if current >= owner.values.len() { + return Ok(0); + } + + let actual = std::cmp::min(owner.values.len() - current, values.len()); + let (values, _) = values.split_at_mut(actual); + values.clone_from_slice(&owner.values[current..current + actual]); + + self.current + .fetch_add(actual, std::sync::atomic::Ordering::Relaxed); + + Ok(actual as u32) + } +} + +impl From> for IIterable +where + T: RuntimeType, + T::Default: Clone, +{ + fn from(values: Vec) -> Self { + ComObject::new(StockIterable { values }).into_interface() + } +} diff --git a/third_party/windows-winui/windows-collections/src/key_value_pair.rs b/third_party/windows-winui/windows-collections/src/key_value_pair.rs new file mode 100644 index 0000000000..df50be07c3 --- /dev/null +++ b/third_party/windows-winui/windows-collections/src/key_value_pair.rs @@ -0,0 +1,36 @@ +use super::*; +use windows_core::*; + +pub(super) struct StockKeyValuePair +where + K: RuntimeType + 'static, + V: RuntimeType + 'static, + K::Default: Clone, + V::Default: Clone, +{ + pub(super) key: K::Default, + pub(super) value: V::Default, +} + +implement_decl! { + impl StockKeyValuePair as pub(super) StockKeyValuePair_Impl: [ + IKeyValuePair, + ] + where K: RuntimeType + 'static, V: RuntimeType + 'static, K::Default: Clone, V::Default: Clone +} + +impl IKeyValuePair_Impl for StockKeyValuePair_Impl +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone, + V::Default: Clone, +{ + fn Key(&self) -> Result { + K::from_default(&self.key) + } + + fn Value(&self) -> Result { + V::from_default(&self.value) + } +} diff --git a/third_party/windows-winui/windows-collections/src/lib.rs b/third_party/windows-winui/windows-collections/src/lib.rs new file mode 100644 index 0000000000..8e724ac78f --- /dev/null +++ b/third_party/windows-winui/windows-collections/src/lib.rs @@ -0,0 +1,33 @@ +#![doc = include_str!("../readme.md")] +#![cfg_attr(all(not(feature = "std")), no_std)] +#![expect( + missing_docs, + non_snake_case, + non_camel_case_types, + non_upper_case_globals, + clippy::missing_transmute_annotations, + clippy::type_complexity +)] + +mod bindings; +pub use bindings::*; + +#[cfg(feature = "std")] +const E_BOUNDS: windows_core::HRESULT = windows_core::HRESULT(0x8000000B_u32 as _); + +#[cfg(feature = "std")] +mod iterable; +#[cfg(feature = "std")] +mod key_value_pair; +#[cfg(feature = "std")] +mod map; +#[cfg(feature = "std")] +mod map_view; +#[cfg(feature = "std")] +mod observable_map; +#[cfg(feature = "std")] +mod observable_vector; +#[cfg(feature = "std")] +mod vector; +#[cfg(feature = "std")] +mod vector_view; diff --git a/third_party/windows-winui/windows-collections/src/map.rs b/third_party/windows-winui/windows-collections/src/map.rs new file mode 100644 index 0000000000..79ecdd2a77 --- /dev/null +++ b/third_party/windows-winui/windows-collections/src/map.rs @@ -0,0 +1,194 @@ +use super::*; +use windows_core::*; + +struct StockMap +where + K: RuntimeType + 'static, + V: RuntimeType + 'static, + K::Default: Clone + Ord, + V::Default: Clone, +{ + map: std::sync::RwLock>, +} + +implement_decl! { + impl StockMap as StockMap_Impl: [ + IMap, + IIterable>, + ] + where K: RuntimeType + 'static, V: RuntimeType + 'static, K::Default: Clone + Ord, V::Default: Clone +} + +impl IIterable_Impl> for StockMap_Impl +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone + Ord, + V::Default: Clone, +{ + fn First(&self) -> Result>> { + Ok(ComObject::new(StockMapIterator:: { + owner: self.to_object(), + current: 0.into(), + }) + .into_interface()) + } +} + +impl IMap_Impl for StockMap_Impl +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone + Ord, + V::Default: Clone, +{ + fn Lookup(&self, key: Ref) -> Result { + let map = self.map.read().unwrap(); + let value = map + .get(ref_as_default::(&key)) + .ok_or_else(|| Error::from(E_BOUNDS))?; + V::from_default(value) + } + + fn Size(&self) -> Result { + Ok(self.map.read().unwrap().len().try_into()?) + } + + fn HasKey(&self, key: Ref) -> Result { + Ok(self + .map + .read() + .unwrap() + .contains_key(ref_as_default::(&key))) + } + + fn GetView(&self) -> Result> { + let snapshot = self.map.read().unwrap().clone(); + Ok(IMapView::::from(snapshot)) + } + + fn Insert(&self, key: Ref, value: Ref) -> Result { + let mut map = self.map.write().unwrap(); + let replaced = map.contains_key(ref_as_default::(&key)); + map.insert( + ref_as_default::(&key).clone(), + ref_as_default::(&value).clone(), + ); + Ok(replaced) + } + + fn Remove(&self, key: Ref) -> Result<()> { + let mut map = self.map.write().unwrap(); + if map.remove(ref_as_default::(&key)).is_none() { + return Err(Error::from(E_BOUNDS)); + } + Ok(()) + } + + fn Clear(&self) -> Result<()> { + self.map.write().unwrap().clear(); + Ok(()) + } +} + +struct StockMapIterator +where + K: RuntimeType + 'static, + V: RuntimeType + 'static, + K::Default: Clone + Ord, + V::Default: Clone, +{ + owner: ComObject>, + current: std::sync::atomic::AtomicUsize, +} + +implement_decl! { + impl StockMapIterator as StockMapIterator_Impl: [ + IIterator>, + ] + where K: RuntimeType + 'static, V: RuntimeType + 'static, K::Default: Clone + Ord, V::Default: Clone +} + +impl IIterator_Impl> for StockMapIterator_Impl +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone + Ord, + V::Default: Clone, +{ + fn Current(&self) -> Result> { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let map = self.owner.map.read().unwrap(); + if let Some((key, value)) = map.iter().nth(current) { + Ok(ComObject::new(super::key_value_pair::StockKeyValuePair { + key: key.clone(), + value: value.clone(), + }) + .into_interface()) + } else { + Err(Error::from(E_BOUNDS)) + } + } + + fn HasCurrent(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let map = self.owner.map.read().unwrap(); + Ok(map.len() > current) + } + + fn MoveNext(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let map = self.owner.map.read().unwrap(); + let len = map.len(); + drop(map); + + if current < len { + self.current + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + } + + Ok(len > current + 1) + } + + fn GetMany(&self, items: &mut [Option>]) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let map = self.owner.map.read().unwrap(); + + if current >= map.len() { + return Ok(0); + } + + let actual = std::cmp::min(map.len() - current, items.len()); + let (items, _) = items.split_at_mut(actual); + + for (item, (key, value)) in items.iter_mut().zip(map.iter().skip(current)) { + *item = Some( + ComObject::new(super::key_value_pair::StockKeyValuePair { + key: key.clone(), + value: value.clone(), + }) + .into_interface(), + ); + } + + self.current + .fetch_add(actual, std::sync::atomic::Ordering::Relaxed); + + Ok(actual as u32) + } +} + +impl From> for IMap +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone + Ord, + V::Default: Clone, +{ + fn from(map: std::collections::BTreeMap) -> Self { + ComObject::new(StockMap { + map: std::sync::RwLock::new(map), + }) + .into_interface() + } +} diff --git a/third_party/windows-winui/windows-collections/src/map_view.rs b/third_party/windows-winui/windows-collections/src/map_view.rs new file mode 100644 index 0000000000..e7e8b7d14e --- /dev/null +++ b/third_party/windows-winui/windows-collections/src/map_view.rs @@ -0,0 +1,161 @@ +use super::*; +use windows_core::*; + +struct StockMapView +where + K: RuntimeType + 'static, + V: RuntimeType + 'static, + K::Default: Clone + Ord, + V::Default: Clone, +{ + map: std::collections::BTreeMap, +} + +implement_decl! { + impl StockMapView as StockMapView_Impl: [ + IMapView, + IIterable>, + ] + where K: RuntimeType + 'static, V: RuntimeType + 'static, K::Default: Clone + Ord, V::Default: Clone +} + +impl IIterable_Impl> for StockMapView_Impl +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone + Ord, + V::Default: Clone, +{ + fn First(&self) -> Result>> { + Ok(ComObject::new(StockMapViewIterator:: { + owner: self.to_object(), + current: 0.into(), + }) + .into_interface()) + } +} + +impl IMapView_Impl for StockMapView_Impl +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone + Ord, + V::Default: Clone, +{ + fn Lookup(&self, key: Ref) -> Result { + let value = self + .map + .get(ref_as_default::(&key)) + .ok_or_else(|| Error::from(E_BOUNDS))?; + + V::from_default(value) + } + + fn Size(&self) -> Result { + Ok(self.map.len().try_into()?) + } + + fn HasKey(&self, key: Ref) -> Result { + Ok(self.map.contains_key(ref_as_default::(&key))) + } + + fn Split(&self, first: OutRef>, second: OutRef>) -> Result<()> { + _ = first.write(None); + _ = second.write(None); + Ok(()) + } +} + +struct StockMapViewIterator +where + K: RuntimeType + 'static, + V: RuntimeType + 'static, + K::Default: Clone + Ord, + V::Default: Clone, +{ + owner: ComObject>, + current: std::sync::atomic::AtomicUsize, +} + +implement_decl! { + impl StockMapViewIterator as StockMapViewIterator_Impl: [ + IIterator>, + ] + where K: RuntimeType + 'static, V: RuntimeType + 'static, K::Default: Clone + Ord, V::Default: Clone +} + +impl IIterator_Impl> for StockMapViewIterator_Impl +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone + Ord, + V::Default: Clone, +{ + fn Current(&self) -> Result> { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + if let Some((key, value)) = self.owner.map.iter().nth(current) { + Ok(ComObject::new(super::key_value_pair::StockKeyValuePair { + key: key.clone(), + value: value.clone(), + }) + .into_interface()) + } else { + Err(Error::from(E_BOUNDS)) + } + } + + fn HasCurrent(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + Ok(self.owner.map.len() > current) + } + + fn MoveNext(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let len = self.owner.map.len(); + + if current < len { + self.current + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + } + + Ok(len > current + 1) + } + + fn GetMany(&self, pairs: &mut [Option>]) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + + if current >= self.owner.map.len() { + return Ok(0); + } + + let actual = std::cmp::min(self.owner.map.len() - current, pairs.len()); + let (pairs, _) = pairs.split_at_mut(actual); + + for (pair, (key, value)) in pairs.iter_mut().zip(self.owner.map.iter().skip(current)) { + *pair = Some( + ComObject::new(super::key_value_pair::StockKeyValuePair { + key: key.clone(), + value: value.clone(), + }) + .into_interface(), + ); + } + + self.current + .fetch_add(actual, std::sync::atomic::Ordering::Relaxed); + + Ok(actual as u32) + } +} + +impl From> for IMapView +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone + Ord, + V::Default: Clone, +{ + fn from(map: std::collections::BTreeMap) -> Self { + ComObject::new(StockMapView { map }).into_interface() + } +} diff --git a/third_party/windows-winui/windows-collections/src/observable_map.rs b/third_party/windows-winui/windows-collections/src/observable_map.rs new file mode 100644 index 0000000000..103ea896ff --- /dev/null +++ b/third_party/windows-winui/windows-collections/src/observable_map.rs @@ -0,0 +1,277 @@ +use super::*; +use windows_core::*; + +struct StockObservableMap +where + K: RuntimeType + 'static, + V: RuntimeType + 'static, + K::Default: Clone + Ord, + V::Default: Clone, +{ + map: std::sync::RwLock>, + handlers: Event>, +} + +implement_decl! { + impl StockObservableMap as StockObservableMap_Impl: [ + IObservableMap, + IMap, + IIterable>, + ] + where K: RuntimeType + 'static, V: RuntimeType + 'static, K::Default: Clone + Ord, V::Default: Clone +} + +impl IObservableMap_Impl for StockObservableMap_Impl +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone + Ord, + V::Default: Clone, +{ + fn MapChanged(&self, vhnd: Ref>) -> Result { + self.handlers.add(vhnd.ok()?) + } + + fn RemoveMapChanged(&self, token: i64) -> Result<()> { + self.handlers.remove(token); + Ok(()) + } +} + +impl IIterable_Impl> for StockObservableMap_Impl +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone + Ord, + V::Default: Clone, +{ + fn First(&self) -> Result>> { + Ok(ComObject::new(StockObservableMapIterator:: { + owner: self.to_object(), + current: 0.into(), + }) + .into_interface()) + } +} + +impl IMap_Impl for StockObservableMap_Impl +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone + Ord, + V::Default: Clone, +{ + fn Lookup(&self, key: Ref) -> Result { + let map = self.map.read().unwrap(); + let value = map + .get(ref_as_default::(&key)) + .ok_or_else(|| Error::from(E_BOUNDS))?; + V::from_default(value) + } + + fn Size(&self) -> Result { + Ok(self.map.read().unwrap().len().try_into()?) + } + + fn HasKey(&self, key: Ref) -> Result { + Ok(self + .map + .read() + .unwrap() + .contains_key(ref_as_default::(&key))) + } + + fn GetView(&self) -> Result> { + let snapshot = self.map.read().unwrap().clone(); + Ok(IMapView::::from(snapshot)) + } + + fn Insert(&self, key: Ref, value: Ref) -> Result { + let replaced = { + let mut map = self.map.write().unwrap(); + let replaced = map.contains_key(ref_as_default::(&key)); + map.insert( + ref_as_default::(&key).clone(), + ref_as_default::(&value).clone(), + ); + replaced + }; + let change = if replaced { + CollectionChange::ItemChanged + } else { + CollectionChange::ItemInserted + }; + self.fire_changed(change, Some(ref_as_default::(&key).clone())); + Ok(replaced) + } + + fn Remove(&self, key: Ref) -> Result<()> { + let key_clone = ref_as_default::(&key).clone(); + { + let mut map = self.map.write().unwrap(); + if map.remove(ref_as_default::(&key)).is_none() { + return Err(Error::from(E_BOUNDS)); + } + } + self.fire_changed(CollectionChange::ItemRemoved, Some(key_clone)); + Ok(()) + } + + fn Clear(&self) -> Result<()> { + self.map.write().unwrap().clear(); + self.fire_changed(CollectionChange::Reset, None); + Ok(()) + } +} + +impl StockObservableMap_Impl +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone + Ord, + V::Default: Clone, +{ + fn fire_changed(&self, change: CollectionChange, key: Option) { + let observable: IObservableMap = self.to_object().into_interface(); + let args: IMapChangedEventArgs = + ComObject::new(StockMapChangedEventArgs { change, key }).into_interface(); + self.handlers + .call(|handler: &MapChangedEventHandler| handler.Invoke(&observable, &args)); + } +} + +struct StockMapChangedEventArgs +where + K: RuntimeType + 'static, + K::Default: Clone, +{ + change: CollectionChange, + key: Option, +} + +implement_decl! { + impl StockMapChangedEventArgs as StockMapChangedEventArgs_Impl: [ + IMapChangedEventArgs, + ] + where K: RuntimeType + 'static, K::Default: Clone +} + +impl IMapChangedEventArgs_Impl for StockMapChangedEventArgs_Impl +where + K: RuntimeType, + K::Default: Clone, +{ + fn CollectionChange(&self) -> Result { + Ok(self.change) + } + + fn Key(&self) -> Result { + match &self.key { + Some(key) => K::from_default(key), + None => Err(Error::from(E_BOUNDS)), + } + } +} + +struct StockObservableMapIterator +where + K: RuntimeType + 'static, + V: RuntimeType + 'static, + K::Default: Clone + Ord, + V::Default: Clone, +{ + owner: ComObject>, + current: std::sync::atomic::AtomicUsize, +} + +implement_decl! { + impl StockObservableMapIterator as StockObservableMapIterator_Impl: [ + IIterator>, + ] + where K: RuntimeType + 'static, V: RuntimeType + 'static, K::Default: Clone + Ord, V::Default: Clone +} + +impl IIterator_Impl> for StockObservableMapIterator_Impl +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone + Ord, + V::Default: Clone, +{ + fn Current(&self) -> Result> { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let map = self.owner.map.read().unwrap(); + if let Some((key, value)) = map.iter().nth(current) { + Ok(ComObject::new(super::key_value_pair::StockKeyValuePair { + key: key.clone(), + value: value.clone(), + }) + .into_interface()) + } else { + Err(Error::from(E_BOUNDS)) + } + } + + fn HasCurrent(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let map = self.owner.map.read().unwrap(); + Ok(map.len() > current) + } + + fn MoveNext(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let map = self.owner.map.read().unwrap(); + let len = map.len(); + drop(map); + + if current < len { + self.current + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + } + + Ok(len > current + 1) + } + + fn GetMany(&self, items: &mut [Option>]) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let map = self.owner.map.read().unwrap(); + + if current >= map.len() { + return Ok(0); + } + + let actual = std::cmp::min(map.len() - current, items.len()); + let (items, _) = items.split_at_mut(actual); + + for (item, (key, value)) in items.iter_mut().zip(map.iter().skip(current)) { + *item = Some( + ComObject::new(super::key_value_pair::StockKeyValuePair { + key: key.clone(), + value: value.clone(), + }) + .into_interface(), + ); + } + + self.current + .fetch_add(actual, std::sync::atomic::Ordering::Relaxed); + + Ok(actual as u32) + } +} + +impl From> for IObservableMap +where + K: RuntimeType, + V: RuntimeType, + K::Default: Clone + Ord, + V::Default: Clone, +{ + fn from(map: std::collections::BTreeMap) -> Self { + ComObject::new(StockObservableMap { + map: std::sync::RwLock::new(map), + handlers: Event::new(), + }) + .into_interface() + } +} diff --git a/third_party/windows-winui/windows-collections/src/observable_vector.rs b/third_party/windows-winui/windows-collections/src/observable_vector.rs new file mode 100644 index 0000000000..31c9aefb02 --- /dev/null +++ b/third_party/windows-winui/windows-collections/src/observable_vector.rs @@ -0,0 +1,295 @@ +use super::*; +use windows_core::*; + +struct StockObservableVector +where + T: RuntimeType + 'static, + T::Default: Clone + PartialEq, +{ + values: std::sync::RwLock>, + handlers: Event>, +} + +implement_decl! { + impl StockObservableVector as StockObservableVector_Impl: [ + IObservableVector, + IVector, + IIterable, + ] + where T: RuntimeType + 'static, T::Default: Clone + PartialEq +} + +impl IObservableVector_Impl for StockObservableVector_Impl +where + T: RuntimeType, + T::Default: Clone + PartialEq, +{ + fn VectorChanged(&self, vhnd: Ref>) -> Result { + self.handlers.add(vhnd.ok()?) + } + + fn RemoveVectorChanged(&self, token: i64) -> Result<()> { + self.handlers.remove(token); + Ok(()) + } +} + +impl IIterable_Impl for StockObservableVector_Impl +where + T: RuntimeType, + T::Default: Clone + PartialEq, +{ + fn First(&self) -> Result> { + Ok(ComObject::new(StockObservableVectorIterator { + owner: self.to_object(), + current: 0.into(), + }) + .into_interface()) + } +} + +impl IVector_Impl for StockObservableVector_Impl +where + T: RuntimeType, + T::Default: Clone + PartialEq, +{ + fn GetAt(&self, index: u32) -> Result { + let values = self.values.read().unwrap(); + let item = values + .get(index as usize) + .ok_or_else(|| Error::from(E_BOUNDS))?; + T::from_default(item) + } + + fn Size(&self) -> Result { + Ok(self.values.read().unwrap().len().try_into()?) + } + + fn GetView(&self) -> Result> { + let snapshot = self.values.read().unwrap().clone(); + Ok(IVectorView::::from(snapshot)) + } + + fn IndexOf(&self, value: Ref, result: &mut u32) -> Result { + let values = self.values.read().unwrap(); + if let Some(index) = values + .iter() + .position(|element| element == ref_as_default::(&value)) + { + *result = index as u32; + Ok(true) + } else { + *result = 0; + Ok(false) + } + } + + fn SetAt(&self, index: u32, value: Ref) -> Result<()> { + { + let mut values = self.values.write().unwrap(); + let item = values + .get_mut(index as usize) + .ok_or_else(|| Error::from(E_BOUNDS))?; + *item = ref_as_default::(&value).clone(); + } + self.fire_changed(CollectionChange::ItemChanged, index); + Ok(()) + } + + fn InsertAt(&self, index: u32, value: Ref) -> Result<()> { + { + let mut values = self.values.write().unwrap(); + let index = index as usize; + if index > values.len() { + return Err(Error::from(E_BOUNDS)); + } + values.insert(index, ref_as_default::(&value).clone()); + } + self.fire_changed(CollectionChange::ItemInserted, index); + Ok(()) + } + + fn RemoveAt(&self, index: u32) -> Result<()> { + { + let mut values = self.values.write().unwrap(); + if index as usize >= values.len() { + return Err(Error::from(E_BOUNDS)); + } + values.remove(index as usize); + } + self.fire_changed(CollectionChange::ItemRemoved, index); + Ok(()) + } + + fn Append(&self, value: Ref) -> Result<()> { + let index = { + let mut values = self.values.write().unwrap(); + values.push(ref_as_default::(&value).clone()); + (values.len() - 1) as u32 + }; + self.fire_changed(CollectionChange::ItemInserted, index); + Ok(()) + } + + fn RemoveAtEnd(&self) -> Result<()> { + let index = { + let mut values = self.values.write().unwrap(); + if values.is_empty() { + return Err(Error::from(E_BOUNDS)); + } + let index = (values.len() - 1) as u32; + values.pop(); + index + }; + self.fire_changed(CollectionChange::ItemRemoved, index); + Ok(()) + } + + fn Clear(&self) -> Result<()> { + self.values.write().unwrap().clear(); + self.fire_changed(CollectionChange::Reset, 0); + Ok(()) + } + + fn GetMany(&self, start_index: u32, items: &mut [T::Default]) -> Result { + let values = self.values.read().unwrap(); + let current = start_index as usize; + + if current >= values.len() { + return Ok(0); + } + + let actual = std::cmp::min(values.len() - current, items.len()); + let (items, _) = items.split_at_mut(actual); + items.clone_from_slice(&values[current..current + actual]); + Ok(actual as u32) + } + + fn ReplaceAll(&self, items: &[T::Default]) -> Result<()> { + { + let mut values = self.values.write().unwrap(); + values.clear(); + values.extend_from_slice(items); + } + self.fire_changed(CollectionChange::Reset, 0); + Ok(()) + } +} + +impl StockObservableVector_Impl +where + T: RuntimeType, + T::Default: Clone + PartialEq, +{ + fn fire_changed(&self, change: CollectionChange, index: u32) { + let observable: IObservableVector = self.to_object().into_interface(); + let args: IVectorChangedEventArgs = + ComObject::new(StockVectorChangedEventArgs { change, index }).into_interface(); + self.handlers + .call(|handler: &VectorChangedEventHandler| handler.Invoke(&observable, &args)); + } +} + +struct StockVectorChangedEventArgs { + change: CollectionChange, + index: u32, +} + +implement_decl! { + impl StockVectorChangedEventArgs as StockVectorChangedEventArgs_Impl: [IVectorChangedEventArgs] +} + +impl IVectorChangedEventArgs_Impl for StockVectorChangedEventArgs_Impl { + fn CollectionChange(&self) -> Result { + Ok(self.change) + } + + fn Index(&self) -> Result { + Ok(self.index) + } +} + +struct StockObservableVectorIterator +where + T: RuntimeType + 'static, + T::Default: Clone + PartialEq, +{ + owner: ComObject>, + current: std::sync::atomic::AtomicUsize, +} + +implement_decl! { + impl StockObservableVectorIterator as StockObservableVectorIterator_Impl: [ + IIterator, + ] + where T: RuntimeType + 'static, T::Default: Clone + PartialEq +} + +impl IIterator_Impl for StockObservableVectorIterator_Impl +where + T: RuntimeType, + T::Default: Clone + PartialEq, +{ + fn Current(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let values = self.owner.values.read().unwrap(); + + if let Some(item) = values.get(current) { + T::from_default(item) + } else { + Err(Error::from(E_BOUNDS)) + } + } + + fn HasCurrent(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let values = self.owner.values.read().unwrap(); + Ok(values.len() > current) + } + + fn MoveNext(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let values = self.owner.values.read().unwrap(); + let len = values.len(); + drop(values); + + if current < len { + self.current + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + } + + Ok(len > current + 1) + } + + fn GetMany(&self, items: &mut [T::Default]) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let values = self.owner.values.read().unwrap(); + + if current >= values.len() { + return Ok(0); + } + + let actual = std::cmp::min(values.len() - current, items.len()); + let (items, _) = items.split_at_mut(actual); + items.clone_from_slice(&values[current..current + actual]); + + self.current + .fetch_add(actual, std::sync::atomic::Ordering::Relaxed); + + Ok(actual as u32) + } +} + +impl From> for IObservableVector +where + T: RuntimeType, + T::Default: Clone + PartialEq, +{ + fn from(values: Vec) -> Self { + ComObject::new(StockObservableVector { + values: std::sync::RwLock::new(values), + handlers: Event::new(), + }) + .into_interface() + } +} diff --git a/third_party/windows-winui/windows-collections/src/vector.rs b/third_party/windows-winui/windows-collections/src/vector.rs new file mode 100644 index 0000000000..686ef96cb6 --- /dev/null +++ b/third_party/windows-winui/windows-collections/src/vector.rs @@ -0,0 +1,224 @@ +use super::*; +use windows_core::*; + +struct StockVector +where + T: RuntimeType + 'static, + T::Default: Clone + PartialEq, +{ + values: std::sync::RwLock>, +} + +implement_decl! { + impl StockVector as StockVector_Impl: [ + IVector, + IIterable, + ] + where T: RuntimeType + 'static, T::Default: Clone + PartialEq +} + +impl IIterable_Impl for StockVector_Impl +where + T: RuntimeType, + T::Default: Clone + PartialEq, +{ + fn First(&self) -> Result> { + Ok(ComObject::new(StockVectorIterator { + owner: self.to_object(), + current: 0.into(), + }) + .into_interface()) + } +} + +impl IVector_Impl for StockVector_Impl +where + T: RuntimeType, + T::Default: Clone + PartialEq, +{ + fn GetAt(&self, index: u32) -> Result { + let values = self.values.read().unwrap(); + let item = values + .get(index as usize) + .ok_or_else(|| Error::from(E_BOUNDS))?; + T::from_default(item) + } + + fn Size(&self) -> Result { + Ok(self.values.read().unwrap().len().try_into()?) + } + + fn GetView(&self) -> Result> { + let snapshot = self.values.read().unwrap().clone(); + Ok(IVectorView::::from(snapshot)) + } + + fn IndexOf(&self, value: Ref, result: &mut u32) -> Result { + let values = self.values.read().unwrap(); + if let Some(index) = values + .iter() + .position(|element| element == ref_as_default::(&value)) + { + *result = index as u32; + Ok(true) + } else { + *result = 0; + Ok(false) + } + } + + fn SetAt(&self, index: u32, value: Ref) -> Result<()> { + let mut values = self.values.write().unwrap(); + let item = values + .get_mut(index as usize) + .ok_or_else(|| Error::from(E_BOUNDS))?; + *item = ref_as_default::(&value).clone(); + Ok(()) + } + + fn InsertAt(&self, index: u32, value: Ref) -> Result<()> { + let mut values = self.values.write().unwrap(); + let index = index as usize; + if index > values.len() { + return Err(Error::from(E_BOUNDS)); + } + values.insert(index, ref_as_default::(&value).clone()); + Ok(()) + } + + fn RemoveAt(&self, index: u32) -> Result<()> { + let mut values = self.values.write().unwrap(); + if index as usize >= values.len() { + return Err(Error::from(E_BOUNDS)); + } + values.remove(index as usize); + Ok(()) + } + + fn Append(&self, value: Ref) -> Result<()> { + self.values + .write() + .unwrap() + .push(ref_as_default::(&value).clone()); + Ok(()) + } + + fn RemoveAtEnd(&self) -> Result<()> { + let mut values = self.values.write().unwrap(); + if values.is_empty() { + return Err(Error::from(E_BOUNDS)); + } + values.pop(); + Ok(()) + } + + fn Clear(&self) -> Result<()> { + self.values.write().unwrap().clear(); + Ok(()) + } + + fn GetMany(&self, start_index: u32, items: &mut [T::Default]) -> Result { + let values = self.values.read().unwrap(); + let current = start_index as usize; + + if current >= values.len() { + return Ok(0); + } + + let actual = std::cmp::min(values.len() - current, items.len()); + let (items, _) = items.split_at_mut(actual); + items.clone_from_slice(&values[current..current + actual]); + Ok(actual as u32) + } + + fn ReplaceAll(&self, items: &[T::Default]) -> Result<()> { + let mut values = self.values.write().unwrap(); + values.clear(); + values.extend_from_slice(items); + Ok(()) + } +} + +struct StockVectorIterator +where + T: RuntimeType + 'static, + T::Default: Clone + PartialEq, +{ + owner: ComObject>, + current: std::sync::atomic::AtomicUsize, +} + +implement_decl! { + impl StockVectorIterator as StockVectorIterator_Impl: [ + IIterator, + ] + where T: RuntimeType + 'static, T::Default: Clone + PartialEq +} + +impl IIterator_Impl for StockVectorIterator_Impl +where + T: RuntimeType, + T::Default: Clone + PartialEq, +{ + fn Current(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let values = self.owner.values.read().unwrap(); + + if let Some(item) = values.get(current) { + T::from_default(item) + } else { + Err(Error::from(E_BOUNDS)) + } + } + + fn HasCurrent(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let values = self.owner.values.read().unwrap(); + Ok(values.len() > current) + } + + fn MoveNext(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let values = self.owner.values.read().unwrap(); + let len = values.len(); + drop(values); + + if current < len { + self.current + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + } + + Ok(len > current + 1) + } + + fn GetMany(&self, items: &mut [T::Default]) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + let values = self.owner.values.read().unwrap(); + + if current >= values.len() { + return Ok(0); + } + + let actual = std::cmp::min(values.len() - current, items.len()); + let (items, _) = items.split_at_mut(actual); + items.clone_from_slice(&values[current..current + actual]); + + self.current + .fetch_add(actual, std::sync::atomic::Ordering::Relaxed); + + Ok(actual as u32) + } +} + +impl From> for IVector +where + T: RuntimeType, + T::Default: Clone + PartialEq, +{ + fn from(values: Vec) -> Self { + ComObject::new(StockVector { + values: std::sync::RwLock::new(values), + }) + .into_interface() + } +} diff --git a/third_party/windows-winui/windows-collections/src/vector_view.rs b/third_party/windows-winui/windows-collections/src/vector_view.rs new file mode 100644 index 0000000000..7dcf79cc05 --- /dev/null +++ b/third_party/windows-winui/windows-collections/src/vector_view.rs @@ -0,0 +1,153 @@ +use super::*; +use windows_core::*; + +struct StockVectorView +where + T: RuntimeType + 'static, + T::Default: Clone + PartialEq, +{ + values: Vec, +} + +implement_decl! { + impl StockVectorView as StockVectorView_Impl: [ + IVectorView, + IIterable, + ] + where T: RuntimeType + 'static, T::Default: Clone + PartialEq +} + +impl IIterable_Impl for StockVectorView_Impl +where + T: RuntimeType, + T::Default: Clone + PartialEq, +{ + fn First(&self) -> Result> { + Ok(ComObject::new(StockVectorViewIterator { + owner: self.to_object(), + current: 0.into(), + }) + .into_interface()) + } +} + +impl IVectorView_Impl for StockVectorView_Impl +where + T: RuntimeType, + T::Default: Clone + PartialEq, +{ + fn GetAt(&self, index: u32) -> Result { + let item = self + .values + .get(index as usize) + .ok_or_else(|| Error::from(E_BOUNDS))?; + + T::from_default(item) + } + + fn Size(&self) -> Result { + Ok(self.values.len().try_into()?) + } + + fn IndexOf(&self, value: Ref, result: &mut u32) -> Result { + if let Some(index) = self + .values + .iter() + .position(|element| element == ref_as_default::(&value)) + { + *result = index as u32; + Ok(true) + } else { + *result = 0; + Ok(false) + } + } + + fn GetMany(&self, current: u32, values: &mut [T::Default]) -> Result { + let current = current as usize; + + if current >= self.values.len() { + return Ok(0); + } + + let actual = std::cmp::min(self.values.len() - current, values.len()); + let (values, _) = values.split_at_mut(actual); + values.clone_from_slice(&self.values[current..current + actual]); + Ok(actual as u32) + } +} + +struct StockVectorViewIterator +where + T: RuntimeType + 'static, + T::Default: Clone + PartialEq, +{ + owner: ComObject>, + current: std::sync::atomic::AtomicUsize, +} + +implement_decl! { + impl StockVectorViewIterator as StockVectorViewIterator_Impl: [ + IIterator, + ] + where T: RuntimeType + 'static, T::Default: Clone + PartialEq +} + +impl IIterator_Impl for StockVectorViewIterator_Impl +where + T: RuntimeType, + T::Default: Clone + PartialEq, +{ + fn Current(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + + if let Some(item) = self.owner.values.get(current) { + T::from_default(item) + } else { + Err(Error::from(E_BOUNDS)) + } + } + + fn HasCurrent(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + Ok(self.owner.values.len() > current) + } + + fn MoveNext(&self) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + + if current < self.owner.values.len() { + self.current + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + } + + Ok(self.owner.values.len() > current + 1) + } + + fn GetMany(&self, values: &mut [T::Default]) -> Result { + let current = self.current.load(std::sync::atomic::Ordering::Relaxed); + + if current >= self.owner.values.len() { + return Ok(0); + } + + let actual = std::cmp::min(self.owner.values.len() - current, values.len()); + let (values, _) = values.split_at_mut(actual); + values.clone_from_slice(&self.owner.values[current..current + actual]); + + self.current + .fetch_add(actual, std::sync::atomic::Ordering::Relaxed); + + Ok(actual as u32) + } +} + +impl From> for IVectorView +where + T: RuntimeType, + T::Default: Clone + PartialEq, +{ + fn from(values: Vec) -> Self { + ComObject::new(StockVectorView { values }).into_interface() + } +} diff --git a/third_party/windows-winui/windows-core/Cargo.toml b/third_party/windows-winui/windows-core/Cargo.toml new file mode 100644 index 0000000000..fad07968bf --- /dev/null +++ b/third_party/windows-winui/windows-core/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "windows-core" +version = "0.62.2" +edition = "2021" +rust-version = "1.82" +license = "MIT OR Apache-2.0" +description = "Core type support for COM and Windows" +repository = "https://github.com/microsoft/windows-rs" +categories = ["os::windows-apis"] +readme = "readme.md" + +[dependencies] +windows-implement = { path = "../windows-implement", optional = true } +windows-interface = { path = "../windows-interface", optional = true } +windows-link = { path = "../windows-link" } +windows-result = { path = "../windows-result" } +windows-strings = { path = "../windows-strings" } + +[features] +default = ["std", "proc-macros"] +std = ["windows-result/std", "windows-strings/std"] +# Re-exports the `#[implement]` and `#[interface]` proc-macro attributes. +# Disable to drop the `syn`, `quote`, and `proc-macro2` build-time dependencies. +# Bindgen-generated bindings do not require this feature; only hand-written +# uses of `#[implement]` / `#[interface]` do. +proc-macros = ["dep:windows-implement", "dep:windows-interface"] + +[package.metadata.docs.rs] +default-target = "x86_64-pc-windows-msvc" +targets = [] diff --git a/third_party/windows-winui/windows-core/license-apache-2.0 b/third_party/windows-winui/windows-core/license-apache-2.0 new file mode 100644 index 0000000000..b5ed4ecec2 --- /dev/null +++ b/third_party/windows-winui/windows-core/license-apache-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/windows-winui/windows-core/license-mit b/third_party/windows-winui/windows-core/license-mit new file mode 100644 index 0000000000..9e841e7a26 --- /dev/null +++ b/third_party/windows-winui/windows-core/license-mit @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/third_party/windows-winui/windows-core/readme.md b/third_party/windows-winui/windows-core/readme.md new file mode 100644 index 0000000000..8e0af03161 --- /dev/null +++ b/third_party/windows-winui/windows-core/readme.md @@ -0,0 +1,7 @@ +## Core type support for COM and Windows + +The [windows-core](https://crates.io/crates/windows-core) crate provides core type support for the windows-* family of crates. + +* [Getting started](https://kennykerr.ca/rust-getting-started/) +* [Samples](https://github.com/microsoft/windows-rs/tree/master/crates/samples) +* [Releases](https://github.com/microsoft/windows-rs/releases) diff --git a/third_party/windows-winui/windows-core/src/agile_reference.rs b/third_party/windows-winui/windows-core/src/agile_reference.rs new file mode 100644 index 0000000000..ac87458e45 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/agile_reference.rs @@ -0,0 +1,40 @@ +use super::*; +use core::marker::PhantomData; + +/// A type representing an agile reference to a COM/WinRT object. +/// +/// On Windows this wraps an `IAgileReference` obtained from `RoGetAgileReference`, +/// which marshals the object into the appropriate apartment on `resolve`. +/// On non-Windows targets there is no apartment model, so all interface pointers +/// are inherently agile; `AgileReference` simply holds an `IUnknown` reference and +/// `resolve` round-trips it back to the requested interface via `QueryInterface`. +#[repr(transparent)] +#[derive(Clone, PartialEq, Eq)] +pub struct AgileReference(imp::AgileSlot, PhantomData); + +impl AgileReference { + /// Returns the raw COM pointer to the inner object. + pub fn as_raw(&self) -> *mut core::ffi::c_void { + self.0.as_raw() + } + + /// Creates an agile reference to the object. + pub fn new(object: &T) -> Result { + const { assert!(T::UNKNOWN, "AgileReference requires a COM interface") }; + imp::AgileSlot::new(object).map(|slot| Self(slot, PhantomData)) + } + + /// Retrieves a proxy to the target of the `AgileReference` object that may safely be used within any thread context in which get is called. + pub fn resolve(&self) -> Result { + self.0.resolve() + } +} + +unsafe impl Send for AgileReference {} +unsafe impl Sync for AgileReference {} + +impl core::fmt::Debug for AgileReference { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "AgileReference({:?})", self.0) + } +} diff --git a/third_party/windows-winui/windows-core/src/array.rs b/third_party/windows-winui/windows-core/src/array.rs new file mode 100644 index 0000000000..eb0dbad067 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/array.rs @@ -0,0 +1,157 @@ +use super::*; + +/// A WinRT array stores elements contiguously in a heap-allocated buffer. +pub struct Array> { + pub(crate) data: *mut T::Default, + pub(crate) len: u32, +} + +impl> Default for Array { + fn default() -> Self { + Self { + data: core::ptr::null_mut(), + len: 0, + } + } +} + +impl> Array { + /// Creates an empty array. + pub fn new() -> Self { + Self::default() + } + + /// Creates an array of the given length with default values. + pub fn with_len(len: usize) -> Self { + assert!(len < u32::MAX as usize); + let bytes_amount = len + .checked_mul(core::mem::size_of::()) + .expect("Attempted to allocate too large an Array"); + + // WinRT arrays must be allocated with `CoTaskMemAlloc`. + // SAFETY: overflow was checked above; `CoTaskMemAlloc` accepts a zero size. + let data = unsafe { imp::CoTaskMemAlloc(bytes_amount) as *mut T::Default }; + + assert!(!data.is_null(), "Could not successfully allocate for Array"); + + // SAFETY: WinRT types are safe to zero-initialize, and `data` is non-null with capacity + // for `len * size_of::()` bytes. + unsafe { + core::ptr::write_bytes(data, 0, len); + } + + let len = len as u32; + Self { data, len } + } + + /// Creates an array by copying the elements from the slice. + pub fn from_slice(values: &[T::Default]) -> Self + where + T::Default: Clone, + { + let mut array = Self::with_len(values.len()); + array.clone_from_slice(values); + array + } + + /// Creates an array from a pointer and length. The `len` argument is the number of elements, not the number of bytes. + /// # Safety + /// The `data` argument must have been allocated with `CoTaskMemAlloc`. + pub unsafe fn from_raw_parts(data: *mut T::Default, len: u32) -> Self { + Self { data, len } + } + + /// Returns a slice containing the entire array. + pub fn as_slice(&self) -> &[T::Default] { + self + } + + /// Returns `true` if the array is empty. + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Returns the length of the array. + pub fn len(&self) -> usize { + self.len as usize + } + + /// Clears the contents of the array. + pub fn clear(&mut self) { + if self.is_empty() { + return; + } + + let mut data = core::ptr::null_mut(); + let mut len = 0; + + core::mem::swap(&mut data, &mut self.data); + core::mem::swap(&mut len, &mut self.len); + + // SAFETY: `self` has been reset, so any panic in `T`'s destructor only leaks data + // rather than leaving the array in a bad state. The slice is not used again after + // `drop_in_place`, and we have unique access to `data` for `CoTaskMemFree`. + unsafe { + core::ptr::drop_in_place(core::ptr::slice_from_raw_parts_mut(data, len as usize)); + imp::CoTaskMemFree(data as _); + } + } + + #[doc(hidden)] + /// Gets a mutable pointer to the array's length. + /// + /// # Safety + /// + /// This function is safe, but writing to the pointer is not. Calling this without + /// a subsequent call to `set_abi` is likely to leak memory or cause UB. + pub unsafe fn set_abi_len(&mut self) -> *mut u32 { + &mut self.len + } + + #[doc(hidden)] + /// Turns the array into a pointer to its data and its length. + pub fn into_abi(self) -> (*mut T::Abi, u32) { + let abi = (self.data as *mut _, self.len); + core::mem::forget(self); + abi + } +} + +impl> core::ops::Deref for Array { + type Target = [T::Default]; + + fn deref(&self) -> &[T::Default] { + if self.is_empty() { + return &[]; + } + + // SAFETY: `data` is non-null when the array is not empty. + unsafe { core::slice::from_raw_parts(self.data, self.len as usize) } + } +} + +impl> core::ops::DerefMut for Array { + fn deref_mut(&mut self) -> &mut [T::Default] { + if self.is_empty() { + return &mut []; + } + + // SAFETY: `data` is non-null when the array is not empty. + unsafe { core::slice::from_raw_parts_mut(self.data, self.len as usize) } + } +} + +impl> Drop for Array { + fn drop(&mut self) { + self.clear(); + } +} + +impl> core::fmt::Debug for Array +where + T::Default: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + core::ops::Deref::deref(self).fmt(f) + } +} diff --git a/third_party/windows-winui/windows-core/src/as_impl.rs b/third_party/windows-winui/windows-core/src/as_impl.rs new file mode 100644 index 0000000000..75a8b60b83 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/as_impl.rs @@ -0,0 +1,20 @@ +/// A trait for retrieving the implementation behind a COM or WinRT interface. +/// +/// This trait is automatically implemented when using the `implement` macro. +pub trait AsImpl { + /// # Safety + /// + /// The caller needs to ensure that `self` is actually implemented by the + /// implementation `T`. + unsafe fn as_impl(&self) -> &T { + unsafe { self.as_impl_ptr().as_ref() } + } + + /// Returns a pointer to the implementation object. + /// + /// # Safety + /// + /// The caller needs to ensure that `self` is actually implemented by the + /// implementation `T`. + unsafe fn as_impl_ptr(&self) -> core::ptr::NonNull; +} diff --git a/third_party/windows-winui/windows-core/src/com_object.rs b/third_party/windows-winui/windows-core/src/com_object.rs new file mode 100644 index 0000000000..e2b4db459d --- /dev/null +++ b/third_party/windows-winui/windows-core/src/com_object.rs @@ -0,0 +1,383 @@ +use crate::imp::Box; +use crate::{IUnknown, IUnknownImpl, Interface, InterfaceRef}; +use core::any::Any; +use core::borrow::Borrow; +use core::ops::Deref; +use core::ptr::NonNull; + +/// Identifies types that can be placed in [`ComObject`]. +/// +/// This trait links types that can be placed in `ComObject` with the types generated by the +/// `#[implement]` macro. The generated types contain the vtable layouts and refcount-related +/// fields for the COM object implementation. +/// +/// This is an implementation detail of the Windows crates; user code should not interact +/// with this trait directly. +/// +/// This trait is the inverse of [`IUnknownImpl`]: it lets user code use [`ComObject`] +/// instead of `ComObject`. +pub trait ComObjectInner: Sized { + /// The generated `_Impl` type (aka the "boxed" or "outer" type). + type Outer: IUnknownImpl; + + /// Moves an instance of this type into a new `ComObject` box and returns it. + /// + /// # Safety + /// + /// Safe Rust code must never acquire an owned instance of a generated "outer" COM object + /// type (e.g. `_Impl`): such a value carries a reference count and methods that + /// adjust it (destroying the object when it reaches zero). + /// + /// Safe Rust code may only interact with these values through a `ComObject` reference. + /// `ComObject` adjusts reference counts and ties the lifetime of a `&_Impl` to + /// that of the related `ComObject`. + /// + /// The `#[implement]` macro generates the implementation. It encapsulates construction + /// of the `_Impl`, immediately places it on the heap, and returns a `ComObject` + /// reference, preserving the invariant that safe Rust code never owns a `_Impl` + /// directly. + fn into_object(self) -> ComObject; +} + +/// Describes the COM interfaces implemented by a specific COM object. +/// +/// The `#[implement]` macro generates implementations of this trait on the "outer" types +/// (e.g. `MyApp_Impl`). Each implementation knows how to locate the interface-specific field +/// within `MyApp_Impl`. +/// +/// This is an implementation detail of the Windows crates; user code should not interact +/// with this trait directly. +pub trait ComObjectInterface { + /// Gets a borrowed interface that is implemented by `T`. + fn as_interface_ref(&self) -> InterfaceRef<'_, I>; +} + +/// A counted pointer to a heap-allocated type that implements COM interfaces. +/// +/// This type lets you place an object onto the heap and query for COM interfaces without +/// losing the safe reference to the implementation object. +/// +/// Because the pointer inside is non-null, `Option>` is the same size as a +/// single pointer. +/// +/// # Safety +/// +/// The contained `ptr` is an owned, reference-counted pointer to a _pinned_ +/// `Pin>`. The implementation does not currently use `Pin` directly but is +/// careful not to expose unsafe semantics to safe code; callers of unsafe functions on +/// [`ComObject`] must preserve these invariants. +#[repr(transparent)] +pub struct ComObject { + ptr: NonNull, +} + +impl ComObject { + /// Allocates a heap cell (box) and moves `value` into it. Returns a counted pointer to `value`. + pub fn new(value: T) -> Self { + T::into_object(value) + } + + /// Creates a new `ComObject` from an existing boxed instance. + /// + /// # Safety + /// + /// `ptr` must point to a valid, heap-allocated `T::Outer` (typically from + /// `Box::into_raw(Box::new(...))`). + /// + /// The pointed-to box must have a reference count greater than zero. + /// + /// This takes ownership of the existing pointer; it does not call `AddRef`. The reference + /// count must accurately reflect all outstanding references to the box, including `ptr`. + pub unsafe fn from_raw(ptr: NonNull) -> Self { + Self { ptr } + } + + /// Gets a reference to the shared object stored in the box. + /// + /// [`ComObject`] also implements [`Deref`], so it is often more convenient to deref + /// directly. Use this method when the [`Deref`] impl is inconvenient. + #[inline(always)] + pub fn get(&self) -> &T { + self.get_box().get_impl() + } + + /// Gets a reference to the shared object's heap box. + #[inline(always)] + fn get_box(&self) -> &T::Outer { + unsafe { self.ptr.as_ref() } + } + + // We _do not_ provide a way to get a mutable reference to the outer box. Returning `&mut T` + // is OK, but `&mut T::Outer` is not: it would allow replacing the entire object (box and + // reference count), which could lead to UB. This could perhaps be solved by returning + // `Pin<&mut T::Outer>`, but that requires additional thought. + + /// Gets a mutable reference to the object stored in the box, if the reference count is + /// exactly 1. Returns `None` if there are multiple references. + #[inline(always)] + pub fn get_mut(&mut self) -> Option<&mut T> { + if self.is_reference_count_one() { + // SAFETY: Must only return `&mut T`, *NOT* `&mut T::Outer`. Returning `T::Outer` + // would allow swapping the contents of the object, incorrectly modifying the + // reference count. + unsafe { Some(self.ptr.as_mut().get_impl_mut()) } + } else { + None + } + } + + /// If this object has only a single reference (i.e. this [`ComObject`] is the only + /// reference to the heap allocation), extracts the inner `T` and frees the heap allocation. + /// Returns `Err(self)` if there is more than one reference. + #[inline(always)] + pub fn take(self) -> Result { + if self.is_reference_count_one() { + let outer_box: Box = unsafe { core::mem::transmute(self) }; + Ok(outer_box.into_inner()) + } else { + Err(self) + } + } + + /// Casts to the given interface type. + /// + /// This always performs a `QueryInterface`, even if `T` is known to implement `I`. If you + /// know that `T` implements `I`, use [`Self::as_interface`] or [`Self::to_interface`] + /// instead to avoid the dynamic `QueryInterface` call. + #[inline(always)] + pub fn cast(&self) -> windows_core::Result + where + T::Outer: ComObjectInterface, + { + let unknown = self.as_interface::(); + unknown.cast() + } + + /// Gets a borrowed reference to an interface that is implemented by `T`. + /// + /// The returned reference is not `AddRef`ed; call [`InterfaceRef::to_owned`] to obtain + /// an owned reference. + #[inline(always)] + pub fn as_interface(&self) -> InterfaceRef<'_, I> + where + T::Outer: ComObjectInterface, + { + self.get_box().as_interface_ref() + } + + /// Gets an owned (counted) reference to an interface that is implemented by this [`ComObject`]. + #[inline(always)] + pub fn to_interface(&self) -> I + where + T::Outer: ComObjectInterface, + { + self.as_interface::().to_owned() + } + + /// Converts `self` into an interface that it implements. + /// + /// This does not need to adjust reference counts because `self` is consumed. + #[inline(always)] + pub fn into_interface(self) -> I + where + T::Outer: ComObjectInterface, + { + unsafe { + let raw = self.get_box().as_interface_ref().as_raw(); + core::mem::forget(self); + I::from_raw(raw) + } + } + + /// Casts the given COM interface to `&dyn Any`, returning a reference to the "outer" + /// object (e.g. `MyApp_Impl`), not the inner `MyApp`. + /// + /// `T` must be a type annotated with `#[implement]`; this is enforced at compile time by + /// the generic constraints. + /// + /// Returns `Err(E_NOINTERFACE)` if the object is not a Rust object, not `T`, or contains + /// non-static lifetimes. + /// + /// The returned value is an owned (counted) reference: this function calls `AddRef`. If + /// you do not need an owned reference, use [`Interface::cast_object_ref`] instead to + /// avoid the `AddRef` / `Release` overhead. + pub fn cast_from(interface: &I) -> crate::Result + where + I: Interface, + T::Outer: Any + 'static + IUnknownImpl, + { + interface.cast_object() + } +} + +impl Default for ComObject { + fn default() -> Self { + Self::new(T::default()) + } +} + +impl Drop for ComObject { + fn drop(&mut self) { + unsafe { + T::Outer::Release(self.ptr.as_ptr()); + } + } +} + +impl Clone for ComObject { + #[inline(always)] + fn clone(&self) -> Self { + unsafe { + self.ptr.as_ref().AddRef(); + Self { ptr: self.ptr } + } + } +} + +impl AsRef for ComObject { + #[inline(always)] + fn as_ref(&self) -> &T { + self.get() + } +} + +impl Deref for ComObject { + type Target = T::Outer; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.get_box() + } +} + +// There is no DerefMut implementation because we cannot statically guarantee +// that the reference count is 1, which is a requirement for getting exclusive +// access to the contents of the object. Use get_mut() for dynamically-checked +// exclusive access. + +impl From for ComObject { + fn from(value: T) -> Self { + Self::new(value) + } +} + +// Delegate hashing, if implemented. +impl core::hash::Hash for ComObject { + fn hash(&self, state: &mut H) { + self.get().hash(state); + } +} + +// If T is Send (or Sync) then the ComObject is also Send (or Sync). +// Since the actual object storage is in the heap, the object is never moved. +unsafe impl Send for ComObject {} +unsafe impl Sync for ComObject {} + +impl PartialEq for ComObject { + fn eq(&self, other: &Self) -> bool { + let inner_self: &T = self.get(); + let other_self: &T = other.get(); + inner_self == other_self + } +} + +impl Eq for ComObject {} + +impl PartialOrd for ComObject { + fn partial_cmp(&self, other: &Self) -> Option { + let inner_self: &T = self.get(); + let other_self: &T = other.get(); + ::partial_cmp(inner_self, other_self) + } +} + +impl Ord for ComObject { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + let inner_self: &T = self.get(); + let other_self: &T = other.get(); + ::cmp(inner_self, other_self) + } +} + +impl core::fmt::Debug for ComObject { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + ::fmt(self.get(), f) + } +} + +impl core::fmt::Display for ComObject { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + ::fmt(self.get(), f) + } +} + +impl Borrow for ComObject { + fn borrow(&self) -> &T { + self.get() + } +} + +/// Enables defining COM objects in static storage, useful for factory objects, stateless +/// objects, or objects that contain mutable global state. +/// +/// Unlike [`ComObject`], `StaticComObject` storage is placed directly in static memory +/// rather than on the heap. +/// +/// `StaticComObject`s have a reference count that is adjusted when owned COM interface +/// references (e.g. `IFoo` and `IUnknown`) are created. The reference count is initialized +/// to 1. +/// +/// # Example +/// +/// ```rust,ignore +/// #[implement(IFoo)] +/// struct MyApp { +/// // ... +/// } +/// +/// static MY_STATIC_APP: StaticComObject = MyApp { ... }.into_static(); +/// +/// fn get_my_static_ifoo() -> IFoo { +/// MY_STATIC_APP.to_interface() +/// } +/// ``` +pub struct StaticComObject +where + T: ComObjectInner, +{ + outer: T::Outer, +} + +// IMPORTANT: Do not expose methods that return mutable access to the contents of +// `StaticComObject`. Doing so would violate our safety invariants — for example, a `DerefMut` +// impl would be unsound. +impl StaticComObject +where + T: ComObjectInner, +{ + /// Wraps `outer` in a `StaticComObject`. + pub const fn from_outer(outer: T::Outer) -> Self { + Self { outer } + } +} + +impl StaticComObject +where + T: ComObjectInner, +{ + /// Gets access to the contained value. + pub const fn get(&'static self) -> &'static T::Outer { + &self.outer + } +} + +impl core::ops::Deref for StaticComObject +where + T: ComObjectInner, +{ + type Target = T::Outer; + + fn deref(&self) -> &Self::Target { + &self.outer + } +} diff --git a/third_party/windows-winui/windows-core/src/compose.rs b/third_party/windows-winui/windows-core/src/compose.rs new file mode 100644 index 0000000000..391a095779 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/compose.rs @@ -0,0 +1,62 @@ +use super::*; + +/// A storage cell for the inner non-delegating `IInspectable` produced by COM aggregation +/// (composition). +/// +/// Laid out as `#[repr(transparent)]` over `Option` so callers can transmute +/// a `&mut ComposeBase` into a `&mut Option`. The wrapper exists to satisfy +/// `Send`/`Sync` for objects that embed it as a field but never participate in aggregation. +/// +/// # Safety contract +/// +/// The slot is written exactly once, during the call to [`Compose::compose`], before the +/// outer object is shared. After that point it is only read (during `IUnknown::QueryInterface` +/// fall-through) through a shared reference. Any object using this slot must be agile or +/// otherwise guarantee that the inner `IInspectable` may be observed from multiple threads. +#[doc(hidden)] +#[repr(transparent)] +pub struct ComposeBase(Option); + +// SAFETY: see the type-level safety contract above. +unsafe impl Send for ComposeBase {} +// SAFETY: see the type-level safety contract above. +unsafe impl Sync for ComposeBase {} + +impl ComposeBase { + /// Constructs an empty (non-aggregated) base slot. + #[doc(hidden)] + pub const fn new() -> Self { + Self(None) + } + + /// Returns a shared reference to the underlying `Option`. + #[doc(hidden)] + #[inline] + pub fn as_option(&self) -> &Option { + &self.0 + } +} + +/// A trait used to support aggregation (composition) of WinRT runtime classes. +/// +/// Composable WinRT factory methods take an "outer" `IInspectable` (the controlling +/// unknown) and an out-pointer for the "inner" non-delegating `IInspectable`. When a Rust +/// implementation type derives from a composable WinRT class it must hand the runtime an +/// outer `IInspectable` and provide a slot for the runtime to write back the inner. +/// +/// The `windows-implement` proc macro emits an implementation of this trait for every type +/// marked with `#[implement(...)]`. +#[doc(hidden)] +pub trait Compose: Sized { + /// Returns the outer `IInspectable` for `implementation` together with a mutable + /// reference to the slot where the runtime should write back the inner non-delegating + /// `IInspectable`. + /// + /// # Safety + /// + /// The returned mutable reference points into the heap-allocated outer implementation + /// object that backs the returned `IInspectable`. Callers must keep the `IInspectable` + /// alive (so the slot remains valid) for the duration of the composable factory call + /// that consumes it, and must ensure `'a` does not outlive the returned `IInspectable`. + unsafe fn compose<'a>(implementation: Self) -> (IInspectable, &'a mut Option); +} diff --git a/third_party/windows-winui/windows-core/src/event.rs b/third_party/windows-winui/windows-core/src/event.rs new file mode 100644 index 0000000000..cd7658e12f --- /dev/null +++ b/third_party/windows-winui/windows-core/src/event.rs @@ -0,0 +1,142 @@ +use super::*; +use core::iter::once; +use std::sync::{Arc, RwLock}; + +/// A type that you can use to declare and implement an event of a specified delegate type. +/// +/// The implementation is thread-safe and designed to avoid contention between events being +/// raised and delegates being added or removed. +pub struct Event { + delegates: RwLock]>>>, +} + +unsafe impl Send for Event {} +unsafe impl Sync for Event {} + +impl Default for Event { + fn default() -> Self { + Self::new() + } +} + +impl Event { + /// Creates a new, empty `Event`. + pub const fn new() -> Self { + Self { + delegates: RwLock::new(None), + } + } + + /// Registers a delegate with the event object. + pub fn add(&self, delegate: &T) -> Result { + let new_delegate = Delegate::new(delegate)?; + let token = new_delegate.to_token(); + let new_iter = once(new_delegate); + let mut guard = self.delegates.write().unwrap(); + + let new_list = if let Some(old_delegates) = guard.as_ref() { + Arc::from_iter(old_delegates.iter().cloned().chain(new_iter)) + } else { + Arc::from_iter(new_iter) + }; + + let old_list = guard.replace(new_list); + drop(guard); + drop(old_list); // drop the old delegates _after_ releasing lock + + Ok(token) + } + + /// Revokes a delegate's registration from the event object. + pub fn remove(&self, token: i64) { + let mut guard = self.delegates.write().unwrap(); + let mut old_list = None; + if let Some(old_delegates) = guard.as_ref() { + // `self.delegates` is only modified if the token is found. + if let Some(i) = old_delegates + .iter() + .position(|old_delegate| old_delegate.to_token() == token) + { + let new_list = Arc::from_iter( + old_delegates[..i] + .iter() + .chain(old_delegates[i + 1..].iter()) + .cloned(), + ); + + old_list = guard.replace(new_list); + } + } + drop(guard); + drop(old_list); // drop the old delegates _after_ releasing lock + } + + /// Clears the event, removing all delegates. + pub fn clear(&self) { + let mut guard = self.delegates.write().unwrap(); + let old_list = guard.take(); + drop(guard); + drop(old_list); // drop the old delegates _after_ releasing lock + } + + /// Invokes all of the event object's registered delegates with the provided callback. + pub fn call Result<()>>(&self, mut callback: F) { + let delegates = { + let guard = self.delegates.read().unwrap(); + if let Some(delegates) = guard.as_ref() { + delegates.clone() + } else { + // No delegates to call. + return; + } + // <-- lock is released here + }; + + for delegate in delegates.iter() { + if let Err(error) = delegate.call(&mut callback) { + const RPC_E_SERVER_UNAVAILABLE: HRESULT = HRESULT(-2147023174); // HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) + if matches!( + error.code(), + imp::RPC_E_DISCONNECTED | imp::JSCRIPT_E_CANTEXECUTE | RPC_E_SERVER_UNAVAILABLE + ) { + self.remove(delegate.to_token()); + } + } + } + } +} + +/// Holds either a direct or indirect reference to a delegate. A direct reference is typically +/// agile while an indirect reference is an agile wrapper. +#[derive(Clone)] +enum Delegate { + Direct(T), + Indirect(AgileReference), +} + +impl Delegate { + /// Creates a new `Delegate`, containing a suitable reference to the specified delegate. + fn new(delegate: &T) -> Result { + if delegate.cast::().is_ok() { + Ok(Self::Direct(delegate.clone())) + } else { + Ok(Self::Indirect(AgileReference::new(delegate)?)) + } + } + + /// Returns an encoded token to identify the delegate. + fn to_token(&self) -> i64 { + match self { + Self::Direct(delegate) => imp::encode_pointer(delegate.as_raw()), + Self::Indirect(delegate) => imp::encode_pointer(delegate.as_raw()), + } + } + + /// Invokes the delegates with the provided callback. + fn call Result<()>>(&self, mut callback: F) -> Result<()> { + match self { + Self::Direct(delegate) => callback(delegate), + Self::Indirect(delegate) => callback(&delegate.resolve()?), + } + } +} diff --git a/third_party/windows-winui/windows-core/src/event_revoker.rs b/third_party/windows-winui/windows-core/src/event_revoker.rs new file mode 100644 index 0000000000..c8bff06283 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/event_revoker.rs @@ -0,0 +1,59 @@ +use super::*; +use core::ffi::c_void; + +/// A handle that automatically revokes an event registration when dropped. +/// +/// Obtained by calling an event-registration method generated with the +/// `--minimal` bindgen option. The registration is revoked when the +/// `EventRevoker` is dropped. +/// +/// Call [`into_token`] to take back the raw token and prevent the automatic +/// revocation, which is useful for interoperating with code that manages +/// registration tokens directly. +/// +/// [`into_token`]: EventRevoker::into_token +#[must_use = "event registrations are revoked when the EventRevoker is dropped"] +pub struct EventRevoker { + source: IUnknown, + token: i64, + remove: unsafe extern "system" fn(*mut c_void, i64) -> HRESULT, +} + +impl EventRevoker { + #[doc(hidden)] + pub fn new( + source: I, + token: i64, + remove: unsafe extern "system" fn(*mut c_void, i64) -> HRESULT, + ) -> Self { + let source = unsafe { IUnknown::from_raw(source.into_raw()) }; + Self { + source, + token, + remove, + } + } + + /// Consumes the revoker and returns the raw registration token without + /// revoking the event handler. + /// + /// After this call the automatic revocation on drop is cancelled. The + /// caller is responsible for passing the returned token to the + /// corresponding `Remove*` method when the handler is no longer needed. + pub fn into_token(self) -> i64 { + let mut this = core::mem::ManuallyDrop::new(self); + let token = this.token; + // Release the source interface reference without calling Remove*. + unsafe { core::ptr::drop_in_place(&mut this.source) }; + token + } +} + +impl Drop for EventRevoker { + fn drop(&mut self) { + // Best-effort: discard errors silently (Drop cannot return Result). + unsafe { + let _ = (self.remove)(self.source.as_raw(), self.token); + } + } +} diff --git a/third_party/windows-winui/windows-core/src/guid.rs b/third_party/windows-winui/windows-core/src/guid.rs new file mode 100644 index 0000000000..f4b9657560 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/guid.rs @@ -0,0 +1,207 @@ +use super::*; + +/// A globally unique identifier ([GUID](https://docs.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid)) +/// used to identify COM and WinRT interfaces. +#[repr(C)] +#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)] +pub struct GUID { + /// Specifies the first 8 hexadecimal digits. + pub data1: u32, + + /// Specifies the first group of 4 hexadecimal digits. + pub data2: u16, + + /// Specifies the second group of 4 hexadecimal digits. + pub data3: u16, + + /// The first 2 bytes contain the third group of 4 hexadecimal digits. The remaining 6 bytes contain the final 12 hexadecimal digits. + pub data4: [u8; 8], +} + +impl GUID { + /// Creates a unique `GUID` value. + pub fn new() -> Result { + let mut guid = Self::zeroed(); + let result = unsafe { imp::UuidCreate(&mut guid as *mut _ as _) }; + + if matches!(result, 0 | imp::RPC_S_UUID_LOCAL_ONLY) { + Ok(guid) + } else { + Err(Error::from_hresult(WIN32_ERROR(result as u32).to_hresult())) + } + } + + /// Creates a `GUID` represented by the all-zero byte-pattern. + pub const fn zeroed() -> Self { + Self { + data1: 0, + data2: 0, + data3: 0, + data4: [0, 0, 0, 0, 0, 0, 0, 0], + } + } + + /// Creates a `GUID` with the given constant values. + pub const fn from_values(data1: u32, data2: u16, data3: u16, data4: [u8; 8]) -> Self { + Self { + data1, + data2, + data3, + data4, + } + } + + /// Creates a `GUID` from a `u128` value. + pub const fn from_u128(uuid: u128) -> Self { + Self { + data1: (uuid >> 96) as u32, + data2: ((uuid >> 80) & 0xffff) as u16, + data3: ((uuid >> 64) & 0xffff) as u16, + data4: (uuid as u64).to_be_bytes(), + } + } + + /// Converts a `GUID` to a `u128` value. + pub const fn to_u128(&self) -> u128 { + ((self.data1 as u128) << 96) + + ((self.data2 as u128) << 80) + + ((self.data3 as u128) << 64) + + u64::from_be_bytes(self.data4) as u128 + } + + /// Creates a `GUID` for a "generic" WinRT type. + pub const fn from_signature(signature: imp::ConstBuffer) -> Self { + let data = imp::ConstBuffer::from_slice(&[ + 0x11, 0xf4, 0x7a, 0xd5, 0x7b, 0x73, 0x42, 0xc0, 0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16, + 0xad, 0xee, + ]); + + let data = data.push_other(signature); + + let bytes = imp::sha1(&data).bytes(); + let first = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]); + + let second = u16::from_be_bytes([bytes[4], bytes[5]]); + let mut third = u16::from_be_bytes([bytes[6], bytes[7]]); + third = (third & 0x0fff) | (5 << 12); + let fourth = (bytes[8] & 0x3f) | 0x80; + + Self::from_values( + first, + second, + third, + [ + fourth, bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], + ], + ) + } +} + +impl RuntimeType for GUID { + const SIGNATURE: imp::ConstBuffer = imp::ConstBuffer::from_slice(b"g16"); + const NAME: imp::ConstBuffer = imp::ConstBuffer::from_slice(b"Guid"); +} + +impl TypeKind for GUID { + type TypeKind = CopyType; +} + +impl core::fmt::Debug for GUID { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!( + f, + "{:08X?}-{:04X?}-{:04X?}-{:02X?}{:02X?}-{:02X?}{:02X?}{:02X?}{:02X?}{:02X?}{:02X?}", + self.data1, + self.data2, + self.data3, + self.data4[0], + self.data4[1], + self.data4[2], + self.data4[3], + self.data4[4], + self.data4[5], + self.data4[6], + self.data4[7] + ) + } +} + +impl TryFrom<&str> for GUID { + type Error = Error; + + fn try_from(from: &str) -> Result { + if from.len() != 36 { + return Err(invalid_guid()); + } + + let bytes = &mut from.bytes(); + let mut guid = Self::zeroed(); + + guid.data1 = try_u32(bytes, true)?; + guid.data2 = try_u16(bytes, true)?; + guid.data3 = try_u16(bytes, true)?; + guid.data4[0] = try_u8(bytes, false)?; + guid.data4[1] = try_u8(bytes, true)?; + guid.data4[2] = try_u8(bytes, false)?; + guid.data4[3] = try_u8(bytes, false)?; + guid.data4[4] = try_u8(bytes, false)?; + guid.data4[5] = try_u8(bytes, false)?; + guid.data4[6] = try_u8(bytes, false)?; + guid.data4[7] = try_u8(bytes, false)?; + + Ok(guid) + } +} + +impl From for GUID { + fn from(value: u128) -> Self { + Self::from_u128(value) + } +} + +impl From for u128 { + fn from(value: GUID) -> Self { + value.to_u128() + } +} + +fn invalid_guid() -> Error { + Error::from_hresult(imp::E_INVALIDARG) +} + +fn try_u32(bytes: &mut core::str::Bytes, delimiter: bool) -> Result { + next(bytes, 8, delimiter).ok_or_else(invalid_guid) +} + +fn try_u16(bytes: &mut core::str::Bytes, delimiter: bool) -> Result { + next(bytes, 4, delimiter) + .map(|value| value as u16) + .ok_or_else(invalid_guid) +} + +fn try_u8(bytes: &mut core::str::Bytes, delimiter: bool) -> Result { + next(bytes, 2, delimiter) + .map(|value| value as u8) + .ok_or_else(invalid_guid) +} + +fn next(bytes: &mut core::str::Bytes, len: usize, delimiter: bool) -> Option { + let mut value: u32 = 0; + + for _ in 0..len { + let digit = bytes.next()?; + + match digit { + b'0'..=b'9' => value = (value << 4) + (digit - b'0') as u32, + b'A'..=b'F' => value = (value << 4) + (digit - b'A' + 10) as u32, + b'a'..=b'f' => value = (value << 4) + (digit - b'a' + 10) as u32, + _ => return None, + } + } + + if delimiter && bytes.next() != Some(b'-') { + None + } else { + Some(value) + } +} diff --git a/third_party/windows-winui/windows-core/src/imp/agile.rs b/third_party/windows-winui/windows-core/src/imp/agile.rs new file mode 100644 index 0000000000..8890338a6e --- /dev/null +++ b/third_party/windows-winui/windows-core/src/imp/agile.rs @@ -0,0 +1,64 @@ +//! Platform abstraction for `AgileReference`'s inner storage. +//! +//! On Windows we wrap an `IAgileReference` obtained from `RoGetAgileReference`. +//! On non-Windows targets there is no apartment model, so any interface pointer +//! is already safe to use from any thread; we hold an `IUnknown` clone and +//! recover the requested interface on `resolve` via `QueryInterface`. + +#[cfg(windows)] +use super::*; +use windows_core::*; + +/// Inner storage for `AgileReference`. Wraps `IAgileReference` on Windows and +/// `IUnknown` elsewhere. +#[cfg(windows)] +#[repr(transparent)] +#[derive(Clone, PartialEq, Eq)] +pub struct AgileSlot(IAgileReference); + +#[cfg(not(windows))] +#[repr(transparent)] +#[derive(Clone, PartialEq, Eq)] +pub struct AgileSlot(IUnknown); + +impl AgileSlot { + /// Creates a new agile slot for the given object. + pub fn new(object: &T) -> Result { + #[cfg(windows)] + unsafe { + RoGetAgileReference( + AGILEREFERENCE_DEFAULT, + &T::IID, + core::mem::transmute::<&T, &IUnknown>(object), + ) + .map(Self) + } + #[cfg(not(windows))] + { + object.cast::().map(Self) + } + } + + /// Resolves the slot back to the requested interface. + pub fn resolve(&self) -> Result { + #[cfg(windows)] + unsafe { + self.0.Resolve() + } + #[cfg(not(windows))] + { + self.0.cast() + } + } + + /// Returns the raw COM pointer. + pub fn as_raw(&self) -> *mut core::ffi::c_void { + self.0.as_raw() + } +} + +impl core::fmt::Debug for AgileSlot { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + self.0.fmt(f) + } +} diff --git a/third_party/windows-winui/windows-core/src/imp/array_proxy.rs b/third_party/windows-winui/windows-core/src/imp/array_proxy.rs new file mode 100644 index 0000000000..9b45ea8b21 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/imp/array_proxy.rs @@ -0,0 +1,38 @@ +use crate::{Array, Type}; + +pub struct ArrayProxy> { + data: *mut *mut T::Default, + len: *mut u32, + temp: core::mem::ManuallyDrop>, +} + +pub unsafe fn array_proxy>(data: *mut *mut T::Default, len: *mut u32) -> ArrayProxy { + ArrayProxy { + data, + len, + temp: core::mem::ManuallyDrop::new(Array::new()), + } +} + +impl> Drop for ArrayProxy { + fn drop(&mut self) { + unsafe { + *self.data = self.temp.data; + *self.len = self.temp.len; + } + } +} + +impl> core::ops::Deref for ArrayProxy { + type Target = Array; + + fn deref(&self) -> &Array { + &self.temp + } +} + +impl> core::ops::DerefMut for ArrayProxy { + fn deref_mut(&mut self) -> &mut Array { + &mut self.temp + } +} diff --git a/third_party/windows-winui/windows-core/src/imp/bindings.rs b/third_party/windows-winui/windows-core/src/imp/bindings.rs new file mode 100644 index 0000000000..8a6f710370 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/imp/bindings.rs @@ -0,0 +1,40 @@ +windows_link::link!("combase.dll" "system" fn CoIncrementMTAUsage(pcookie : *mut CO_MTA_USAGE_COOKIE) -> HRESULT); +windows_link::link!("combase.dll" "system" fn CoTaskMemAlloc(cb : usize) -> *mut core::ffi::c_void); +windows_link::link!("combase.dll" "system" fn CoTaskMemFree(pv : *const core::ffi::c_void)); +windows_link::link!("kernel32.dll" "system" fn EncodePointer(ptr : *const core::ffi::c_void) -> *mut core::ffi::c_void); +windows_link::link!("kernel32.dll" "system" fn FreeLibrary(hlibmodule : HMODULE) -> BOOL); +windows_link::link!("kernel32.dll" "system" fn GetProcAddress(hmodule : HMODULE, lpprocname : PCSTR) -> FARPROC); +windows_link::link!("kernel32.dll" "system" fn LoadLibraryExA(lplibfilename : PCSTR, hfile : HANDLE, dwflags : LOAD_LIBRARY_FLAGS) -> HMODULE); +windows_link::link!("api-ms-win-core-winrt-l1-1-0.dll" "system" fn RoGetActivationFactory(activatableclassid : HSTRING, iid : *const GUID, factory : *mut *mut core::ffi::c_void) -> HRESULT); +windows_link::link!("rpcrt4.dll" "system" fn UuidCreate(uuid : *mut GUID) -> RPC_STATUS); +pub type BOOL = i32; +pub type CO_MTA_USAGE_COOKIE = *mut core::ffi::c_void; +pub type FARPROC = Option isize>; +#[repr(C)] +#[derive(Clone, Copy)] +pub struct GUID { + pub data1: u32, + pub data2: u16, + pub data3: u16, + pub data4: [u8; 8], +} +impl GUID { + pub const fn from_u128(uuid: u128) -> Self { + Self { + data1: (uuid >> 96) as u32, + data2: (uuid >> 80 & 0xffff) as u16, + data3: (uuid >> 64 & 0xffff) as u16, + data4: (uuid as u64).to_be_bytes(), + } + } +} +pub type HANDLE = *mut core::ffi::c_void; +pub type HINSTANCE = *mut core::ffi::c_void; +pub type HMODULE = *mut core::ffi::c_void; +pub type HRESULT = i32; +pub type LOAD_LIBRARY_FLAGS = u32; +pub const LOAD_LIBRARY_SEARCH_DEFAULT_DIRS: LOAD_LIBRARY_FLAGS = 4096u32; +pub type PCSTR = *const u8; +pub type RPC_STATUS = i32; +pub const RPC_S_UUID_LOCAL_ONLY: RPC_STATUS = 1824i32; +pub type HSTRING = *mut core::ffi::c_void; diff --git a/third_party/windows-winui/windows-core/src/imp/can_into.rs b/third_party/windows-winui/windows-core/src/imp/can_into.rs new file mode 100644 index 0000000000..47ee7838c4 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/imp/can_into.rs @@ -0,0 +1,5 @@ +pub trait CanInto: Sized { + const QUERY: bool = false; +} + +impl CanInto for T where T: Clone {} diff --git a/third_party/windows-winui/windows-core/src/imp/com_bindings.rs b/third_party/windows-winui/windows-core/src/imp/com_bindings.rs new file mode 100644 index 0000000000..d8a4aa778b --- /dev/null +++ b/third_party/windows-winui/windows-core/src/imp/com_bindings.rs @@ -0,0 +1,246 @@ +#[inline] +pub unsafe fn RoGetAgileReference( + options: AgileReferenceOptions, + riid: *const windows_core::GUID, + punk: P2, +) -> windows_core::Result +where + P2: windows_core::Param, +{ + windows_core::link!("combase.dll" "system" fn RoGetAgileReference(options : AgileReferenceOptions, riid : *const windows_core::GUID, punk : *mut core::ffi::c_void, ppagilereference : *mut *mut core::ffi::c_void) -> windows_core::HRESULT); + unsafe { + let mut result__ = core::mem::zeroed(); + RoGetAgileReference(options, riid, punk.param().abi(), &mut result__) + .and_then(|| windows_core::Type::from_abi(result__)) + } +} +pub const AGILEREFERENCE_DEFAULT: AgileReferenceOptions = AgileReferenceOptions(0i32); +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct AgileReferenceOptions(pub i32); +pub const CO_E_NOTINITIALIZED: windows_core::HRESULT = windows_core::HRESULT(0x800401F0_u32 as _); +pub const E_INVALIDARG: windows_core::HRESULT = windows_core::HRESULT(0x80070057_u32 as _); +pub const E_NOINTERFACE: windows_core::HRESULT = windows_core::HRESULT(0x80004002_u32 as _); +pub const E_POINTER: windows_core::HRESULT = windows_core::HRESULT(0x80004003_u32 as _); +windows_core::imp::define_interface!( + IAgileObject, + IAgileObject_Vtbl, + 0x94ea2b94_e9cc_49e0_c0ff_ee64ca8f5b90 +); +windows_core::imp::interface_hierarchy!(IAgileObject, windows_core::IUnknown); +#[repr(C)] +#[doc(hidden)] +pub struct IAgileObject_Vtbl { + pub base__: windows_core::IUnknown_Vtbl, +} +pub trait IAgileObject_Impl: windows_core::IUnknownImpl {} +impl IAgileObject_Vtbl { + pub const fn new() -> Self { + Self { + base__: windows_core::IUnknown_Vtbl::new::(), + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +impl windows_core::RuntimeName for IAgileObject {} +windows_core::imp::define_interface!( + IAgileReference, + IAgileReference_Vtbl, + 0xc03f6a43_65a4_9818_987e_e0b810d2a6f2 +); +windows_core::imp::interface_hierarchy!(IAgileReference, windows_core::IUnknown); +impl IAgileReference { + pub unsafe fn Resolve(&self) -> windows_core::Result + where + T: windows_core::Interface, + { + let mut result__ = core::ptr::null_mut(); + unsafe { + (windows_core::Interface::vtable(self).Resolve)( + windows_core::Interface::as_raw(self), + &T::IID, + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IAgileReference_Vtbl { + pub base__: windows_core::IUnknown_Vtbl, + pub Resolve: unsafe extern "system" fn( + *mut core::ffi::c_void, + *const windows_core::GUID, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, +} +pub trait IAgileReference_Impl: windows_core::IUnknownImpl { + fn Resolve( + &self, + riid: *const windows_core::GUID, + ppvobjectreference: *mut *mut core::ffi::c_void, + ) -> windows_core::Result<()>; +} +impl IAgileReference_Vtbl { + pub const fn new() -> Self { + unsafe extern "system" fn Resolve( + this: *mut core::ffi::c_void, + riid: *const windows_core::GUID, + ppvobjectreference: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IAgileReference_Impl::Resolve( + this, + core::mem::transmute_copy(&riid), + core::mem::transmute_copy(&ppvobjectreference), + ) + .into() + } + } + Self { + base__: windows_core::IUnknown_Vtbl::new::(), + Resolve: Resolve::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +impl windows_core::RuntimeName for IAgileReference {} +windows_core::imp::define_interface!( + IWeakReference, + IWeakReference_Vtbl, + 0x00000037_0000_0000_c000_000000000046 +); +windows_core::imp::interface_hierarchy!(IWeakReference, windows_core::IUnknown); +impl IWeakReference { + pub unsafe fn Resolve(&self) -> windows_core::Result + where + T: windows_core::Interface, + { + let mut result__ = core::ptr::null_mut(); + unsafe { + (windows_core::Interface::vtable(self).Resolve)( + windows_core::Interface::as_raw(self), + &T::IID, + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IWeakReference_Vtbl { + pub base__: windows_core::IUnknown_Vtbl, + pub Resolve: unsafe extern "system" fn( + *mut core::ffi::c_void, + *const windows_core::GUID, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, +} +pub trait IWeakReference_Impl: windows_core::IUnknownImpl { + fn Resolve( + &self, + riid: *const windows_core::GUID, + objectreference: *mut *mut core::ffi::c_void, + ) -> windows_core::Result<()>; +} +impl IWeakReference_Vtbl { + pub const fn new() -> Self { + unsafe extern "system" fn Resolve( + this: *mut core::ffi::c_void, + riid: *const windows_core::GUID, + objectreference: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IWeakReference_Impl::Resolve( + this, + core::mem::transmute_copy(&riid), + core::mem::transmute_copy(&objectreference), + ) + .into() + } + } + Self { + base__: windows_core::IUnknown_Vtbl::new::(), + Resolve: Resolve::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +impl windows_core::RuntimeName for IWeakReference {} +windows_core::imp::define_interface!( + IWeakReferenceSource, + IWeakReferenceSource_Vtbl, + 0x00000038_0000_0000_c000_000000000046 +); +windows_core::imp::interface_hierarchy!(IWeakReferenceSource, windows_core::IUnknown); +impl IWeakReferenceSource { + pub unsafe fn GetWeakReference(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).GetWeakReference)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IWeakReferenceSource_Vtbl { + pub base__: windows_core::IUnknown_Vtbl, + pub GetWeakReference: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, +} +pub trait IWeakReferenceSource_Impl: windows_core::IUnknownImpl { + fn GetWeakReference(&self) -> windows_core::Result; +} +impl IWeakReferenceSource_Vtbl { + pub const fn new() -> Self { + unsafe extern "system" fn GetWeakReference< + Identity: IWeakReferenceSource_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + weakreference: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IWeakReferenceSource_Impl::GetWeakReference(this) { + Ok(ok__) => { + weakreference.write(core::mem::transmute(ok__)); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + Self { + base__: windows_core::IUnknown_Vtbl::new::(), + GetWeakReference: GetWeakReference::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +impl windows_core::RuntimeName for IWeakReferenceSource {} +pub const JSCRIPT_E_CANTEXECUTE: windows_core::HRESULT = windows_core::HRESULT(0x89020001_u32 as _); +pub const REGDB_E_CLASSNOTREG: windows_core::HRESULT = windows_core::HRESULT(0x80040154_u32 as _); +pub const RPC_E_DISCONNECTED: windows_core::HRESULT = windows_core::HRESULT(0x80010108_u32 as _); +pub const S_OK: windows_core::HRESULT = windows_core::HRESULT(0x0_u32 as _); diff --git a/third_party/windows-winui/windows-core/src/imp/delegate_box.rs b/third_party/windows-winui/windows-core/src/imp/delegate_box.rs new file mode 100644 index 0000000000..0323a7e4a2 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/imp/delegate_box.rs @@ -0,0 +1,92 @@ +use super::*; +use crate::{IUnknown, Interface, GUID, HRESULT}; +use core::ffi::c_void; + +/// A reference-counted, COM-compatible heap allocation used by generated WinRT delegate types. +/// +/// The bindgen-generated `*::new()` allocates an instance of this type and reinterprets +/// the resulting pointer as the delegate. The generated per-delegate code only needs to provide +/// the delegate-specific `Invoke` thunk; the generic `QueryInterface`, `AddRef`, and `Release` +/// implementations are shared across all delegate types via this helper, removing roughly +/// ~80 lines of identical boilerplate per delegate. +#[repr(C)] +#[doc(hidden)] +pub struct DelegateBox { + pub vtable: *const I::Vtable, + pub invoke: F, + pub count: RefCount, +} + +impl DelegateBox { + /// Creates a new `DelegateBox` with a reference count of 1. + pub const fn new(vtable: *const I::Vtable, invoke: F) -> Self { + Self { + vtable, + invoke, + count: RefCount::new(1), + } + } + + /// Generic `IUnknown::QueryInterface` implementation for delegate boxes. + /// + /// Responds to the delegate's own IID, `IUnknown`, `IAgileObject`, and (on Windows) `IMarshal`. + pub unsafe extern "system" fn QueryInterface( + this: *mut c_void, + iid: *const GUID, + interface: *mut *mut c_void, + ) -> HRESULT { + unsafe { + let this = this as *mut *mut c_void as *mut Self; + + if iid.is_null() || interface.is_null() { + return HRESULT(-2147467261); // E_POINTER + } + + *interface = if *iid == ::IID + || *iid == ::IID + || *iid == ::IID + { + &mut (*this).vtable as *mut _ as _ + } else { + #[cfg(windows)] + if *iid == ::IID { + (*this).count.add_ref(); + return marshaler( + core::mem::transmute(&mut (*this).vtable as *mut _ as *mut c_void), + interface, + ); + } + core::ptr::null_mut() + }; + + if (*interface).is_null() { + HRESULT(-2147467262) // E_NOINTERFACE + } else { + (*this).count.add_ref(); + HRESULT(0) + } + } + } + + /// Generic `IUnknown::AddRef` implementation for delegate boxes. + pub unsafe extern "system" fn AddRef(this: *mut c_void) -> u32 { + unsafe { + let this = this as *mut *mut c_void as *mut Self; + (*this).count.add_ref() + } + } + + /// Generic `IUnknown::Release` implementation for delegate boxes. + pub unsafe extern "system" fn Release(this: *mut c_void) -> u32 { + unsafe { + let this = this as *mut *mut c_void as *mut Self; + let remaining = (*this).count.release(); + + if remaining == 0 { + let _ = Box::from_raw(this); + } + + remaining + } + } +} diff --git a/third_party/windows-winui/windows-core/src/imp/factory_cache.rs b/third_party/windows-winui/windows-core/src/imp/factory_cache.rs new file mode 100644 index 0000000000..78a16e557a --- /dev/null +++ b/third_party/windows-winui/windows-core/src/imp/factory_cache.rs @@ -0,0 +1,203 @@ +use super::*; +use crate::Interface; +use core::ffi::c_void; +use core::marker::PhantomData; +use core::mem::{forget, transmute, transmute_copy}; +use core::ptr::null_mut; +use core::sync::atomic::{AtomicPtr, Ordering}; + +pub struct FactoryCache { + shared: AtomicPtr, + _c: PhantomData, + _i: PhantomData, +} + +impl FactoryCache { + pub const fn new() -> Self { + Self { + shared: AtomicPtr::new(null_mut()), + _c: PhantomData, + _i: PhantomData, + } + } +} + +impl Default for FactoryCache { + fn default() -> Self { + Self::new() + } +} + +impl FactoryCache { + pub fn call crate::Result>(&self, callback: F) -> crate::Result { + loop { + let ptr = self.shared.load(Ordering::Relaxed); + if !ptr.is_null() { + return callback(unsafe { transmute::<&*mut c_void, &I>(&ptr) }); + } + + let factory = load_factory::()?; + + // Only agile factories are safe to cache; use non-agile factories once and drop them. + if factory.cast::().is_ok() { + if self + .shared + .compare_exchange_weak( + null_mut(), + factory.as_raw(), + Ordering::Relaxed, + Ordering::Relaxed, + ) + .is_ok() + { + forget(factory); + } + } else { + return callback(&factory); + } + } + } +} + +// `FactoryCache` only holds agile factory pointers, which are safe to share between threads. +unsafe impl Sync for FactoryCache {} + +/// Attempts to load the factory object for the given WinRT class. +/// This can be used to access COM interfaces implemented on a Windows Runtime class factory. +pub fn load_factory() -> crate::Result { + let mut factory: Option = None; + let name = crate::HSTRING::from(C::NAME); + + let code = unsafe { + let mut get_com_factory = || { + crate::HRESULT(RoGetActivationFactory( + transmute_copy(&name), + &I::IID as *const _ as _, + &mut factory as *mut _ as *mut _, + )) + }; + let mut code = get_com_factory(); + + // If combase hasn't been loaded yet, load it automatically so that this "just works" + // for apartment-agnostic code, then retry. + if code == CO_E_NOTINITIALIZED { + let mut cookie = core::ptr::null_mut(); + CoIncrementMTAUsage(&mut cookie); + + code = get_com_factory(); + } + + code + }; + + if let Some(factory) = factory { + return Ok(factory); + } + + // Reg-free activation should only be attempted if the class is not registered. + // It should not be attempted if the class is registered but fails to activate. + if code == REGDB_E_CLASSNOTREG { + if let Some(i) = search_path(C::NAME, |library| unsafe { + get_activation_factory(library, &name) + }) { + return i.cast(); + } + } + + Err(crate::Error::from_hresult(code)) +} + +/// Strips suffix components from `path` (separated by `.`), appending `.dll\0` after each strip, +/// and invokes `callback` until it succeeds. +/// +/// For example, for "A.B.TypeName" the load order is `A.B.dll`, then `A.dll`. +fn search_path(mut path: &str, mut callback: F) -> Option +where + F: FnMut(crate::PCSTR) -> crate::Result, +{ + let suffix = b".dll\0"; + let mut library = alloc::vec![0; path.len() + suffix.len()]; + while let Some(pos) = path.rfind('.') { + path = &path[..pos]; + library.truncate(path.len() + suffix.len()); + library[..path.len()].copy_from_slice(path.as_bytes()); + library[path.len()..].copy_from_slice(suffix); + + if let Ok(r) = callback(crate::PCSTR::from_raw(library.as_ptr())) { + return Some(r); + } + } + + None +} + +unsafe fn get_activation_factory( + library: crate::PCSTR, + name: &crate::HSTRING, +) -> crate::Result { + unsafe { + let function = + delay_load::(library, crate::s!("DllGetActivationFactory")) + .ok_or_else(crate::Error::from_thread)?; + let mut abi = null_mut(); + function(transmute_copy(name), &mut abi).and_then(|| crate::Type::from_abi(abi)) + } +} + +unsafe fn delay_load(library: crate::PCSTR, function: crate::PCSTR) -> Option { + unsafe { + let library = LoadLibraryExA( + library.0, + core::ptr::null_mut(), + LOAD_LIBRARY_SEARCH_DEFAULT_DIRS, + ); + + if library.is_null() { + return None; + } + + let address = GetProcAddress(library, function.0); + + if address.is_some() { + return Some(core::mem::transmute_copy(&address)); + } + + FreeLibrary(library); + None + } +} + +type DllGetActivationFactory = + extern "system" fn(name: *mut c_void, factory: *mut *mut c_void) -> crate::HRESULT; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn dll_search() { + let path = "A.B.TypeName"; + + // Test library successfully found. + let mut results = Vec::new(); + let end_result = search_path(path, |library| { + results.push(unsafe { library.to_string().unwrap() }); + if unsafe { library.as_bytes() } == &b"A.dll"[..] { + Ok(42) + } else { + Err(crate::Error::empty()) + } + }); + assert!(matches!(end_result, Some(42))); + assert_eq!(results, vec!["A.B.dll", "A.dll"]); + + // Test library never successfully found. + let mut results = Vec::new(); + let end_result = search_path(path, |library| { + results.push(unsafe { library.to_string().unwrap() }); + crate::Result::<()>::Err(crate::Error::empty()) + }); + assert!(end_result.is_none()); + assert_eq!(results, vec!["A.B.dll", "A.dll"]); + } +} diff --git a/third_party/windows-winui/windows-core/src/imp/generic_factory.rs b/third_party/windows-winui/windows-core/src/imp/generic_factory.rs new file mode 100644 index 0000000000..1f9ddcee5d --- /dev/null +++ b/third_party/windows-winui/windows-core/src/imp/generic_factory.rs @@ -0,0 +1,33 @@ +use crate::Interface; +use core::ffi::c_void; +use core::mem::{transmute_copy, zeroed}; + +// A streamlined version of the IActivationFactory interface used by WinRT class factories used internally by the windows crate +// to simplify code generation. Components should implement the `IActivationFactory` interface published by the windows crate. +super::define_interface!( + IGenericFactory, + IGenericFactory_Vtbl, + 0x00000035_0000_0000_c000_000000000046 +); +super::interface_hierarchy!(IGenericFactory, crate::IUnknown, crate::IInspectable); + +impl IGenericFactory { + pub fn ActivateInstance(&self) -> crate::Result { + unsafe { + let mut result__ = zeroed(); + (Interface::vtable(self).ActivateInstance)( + transmute_copy(self), + &mut result__ as *mut _ as *mut _, + ) + .and_then(|| crate::Type::from_abi(result__)) + .and_then(|interface: crate::IInspectable| interface.cast()) + } + } +} + +#[repr(C)] +pub struct IGenericFactory_Vtbl { + pub base__: crate::IInspectable_Vtbl, + pub ActivateInstance: + unsafe extern "system" fn(this: *mut c_void, instance: *mut *mut c_void) -> crate::HRESULT, +} diff --git a/third_party/windows-winui/windows-core/src/imp/marshaler.rs b/third_party/windows-winui/windows-core/src/imp/marshaler.rs new file mode 100644 index 0000000000..822cce6505 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/imp/marshaler.rs @@ -0,0 +1,268 @@ +use super::*; +use crate::{IUnknown, IUnknown_Vtbl, Interface, GUID, HRESULT}; +use core::ffi::c_void; +use core::mem::{transmute, transmute_copy}; +use core::ptr::null_mut; + +windows_link::link!("combase.dll" "system" fn CoCreateFreeThreadedMarshaler(punkouter: *mut c_void, ppunkmarshal: *mut *mut c_void) -> HRESULT); + +pub unsafe fn marshaler(outer: IUnknown, result: *mut *mut c_void) -> HRESULT { + unsafe { + let mut marshaler_raw = null_mut(); + _ = CoCreateFreeThreadedMarshaler(null_mut(), &mut marshaler_raw); + assert!(!marshaler_raw.is_null(), "allocation failed"); + let marshaler: IUnknown = transmute(marshaler_raw); + + _ = (marshaler.vtable().QueryInterface)( + transmute_copy(&marshaler), + &IMarshal::IID, + &mut marshaler_raw, + ); + + debug_assert!(!marshaler_raw.is_null()); + let marshaler: IMarshal = transmute(marshaler_raw); + + let marshaler = Marshaler { + vtable: &Marshaler::VTABLE, + outer, + marshaler, + count: RefCount::new(1), + }; + + debug_assert!(!result.is_null()); + *result = transmute::, *mut c_void>(Box::new(marshaler)); + S_OK + } +} + +#[repr(C)] +struct Marshaler { + vtable: *const IMarshal_Vtbl, + outer: IUnknown, + marshaler: IMarshal, + count: RefCount, +} + +impl Marshaler { + const VTABLE: IMarshal_Vtbl = IMarshal_Vtbl { + base__: IUnknown_Vtbl { + QueryInterface: Self::QueryInterface, + AddRef: Self::AddRef, + Release: Self::Release, + }, + GetUnmarshalClass: Self::GetUnmarshalClass, + GetMarshalSizeMax: Self::GetMarshalSizeMax, + MarshalInterface: Self::MarshalInterface, + UnmarshalInterface: Self::UnmarshalInterface, + ReleaseMarshalData: Self::ReleaseMarshalData, + DisconnectObject: Self::DisconnectObject, + }; + + unsafe extern "system" fn QueryInterface( + this: *mut c_void, + iid: *const GUID, + interface: *mut *mut c_void, + ) -> HRESULT { + unsafe { + let this = this as *mut *mut c_void as *mut Self; + + if iid.is_null() || interface.is_null() { + return E_POINTER; + } + + if *iid == IMarshal::IID { + *interface = &mut (*this).vtable as *mut _ as _; + (*this).count.add_ref(); + return S_OK; + } + + ((*this).outer.vtable().QueryInterface)(transmute_copy(&(*this).outer), iid, interface) + } + } + + unsafe extern "system" fn AddRef(this: *mut c_void) -> u32 { + unsafe { + let this = this as *mut *mut c_void as *mut Self; + (*this).count.add_ref() + } + } + + unsafe extern "system" fn Release(this: *mut c_void) -> u32 { + unsafe { + let this = this as *mut *mut c_void as *mut Self; + let remaining = (*this).count.release(); + + if remaining == 0 { + let _ = Box::from_raw(this); + } + + remaining + } + } + + unsafe extern "system" fn GetUnmarshalClass( + this: *mut c_void, + riid: *const GUID, + pv: *const c_void, + dwdestcontext: u32, + pvdestcontext: *const c_void, + mshlflags: u32, + pcid: *mut GUID, + ) -> HRESULT { + unsafe { + let this = this as *mut *mut c_void as *mut Self; + + ((*this).marshaler.vtable().GetUnmarshalClass)( + transmute_copy(&(*this).marshaler), + riid, + pv, + dwdestcontext, + pvdestcontext, + mshlflags, + pcid, + ) + } + } + + unsafe extern "system" fn GetMarshalSizeMax( + this: *mut c_void, + riid: *const GUID, + pv: *const c_void, + dwdestcontext: u32, + pvdestcontext: *const c_void, + mshlflags: u32, + psize: *mut u32, + ) -> HRESULT { + unsafe { + let this = this as *mut *mut c_void as *mut Self; + + ((*this).marshaler.vtable().GetMarshalSizeMax)( + transmute_copy(&(*this).marshaler), + riid, + pv, + dwdestcontext, + pvdestcontext, + mshlflags, + psize, + ) + } + } + + unsafe extern "system" fn MarshalInterface( + this: *mut c_void, + pstm: *mut c_void, + riid: *const GUID, + pv: *const c_void, + dwdestcontext: u32, + pvdestcontext: *const c_void, + mshlflags: u32, + ) -> HRESULT { + unsafe { + let this = this as *mut *mut c_void as *mut Self; + + ((*this).marshaler.vtable().MarshalInterface)( + transmute_copy(&(*this).marshaler), + pstm, + riid, + pv, + dwdestcontext, + pvdestcontext, + mshlflags, + ) + } + } + + unsafe extern "system" fn UnmarshalInterface( + this: *mut c_void, + pstm: *mut c_void, + riid: *const GUID, + ppv: *mut *mut c_void, + ) -> HRESULT { + unsafe { + let this = this as *mut *mut c_void as *mut Self; + + ((*this).marshaler.vtable().UnmarshalInterface)( + transmute_copy(&(*this).marshaler), + pstm, + riid, + ppv, + ) + } + } + + unsafe extern "system" fn ReleaseMarshalData(this: *mut c_void, pstm: *mut c_void) -> HRESULT { + unsafe { + let this = this as *mut *mut c_void as *mut Self; + + ((*this).marshaler.vtable().ReleaseMarshalData)( + transmute_copy(&(*this).marshaler), + pstm, + ) + } + } + + unsafe extern "system" fn DisconnectObject(this: *mut c_void, dwreserved: u32) -> HRESULT { + unsafe { + let this = this as *mut *mut c_void as *mut Self; + + ((*this).marshaler.vtable().DisconnectObject)( + transmute_copy(&(*this).marshaler), + dwreserved, + ) + } + } +} + +#[repr(transparent)] +#[derive(Clone)] +pub struct IMarshal(IUnknown); + +unsafe impl Interface for IMarshal { + type Vtable = IMarshal_Vtbl; + const IID: GUID = GUID::from_u128(0x00000003_0000_0000_c000_000000000046); +} + +#[repr(C)] +pub struct IMarshal_Vtbl { + base__: IUnknown_Vtbl, + + GetUnmarshalClass: unsafe extern "system" fn( + *mut c_void, + *const GUID, + *const c_void, + u32, + *const c_void, + u32, + *mut GUID, + ) -> HRESULT, + + GetMarshalSizeMax: unsafe extern "system" fn( + *mut c_void, + *const GUID, + *const c_void, + u32, + *const c_void, + u32, + *mut u32, + ) -> HRESULT, + + MarshalInterface: unsafe extern "system" fn( + *mut c_void, + *mut c_void, + *const GUID, + *const c_void, + u32, + *const c_void, + u32, + ) -> HRESULT, + + UnmarshalInterface: unsafe extern "system" fn( + *mut c_void, + *mut c_void, + *const GUID, + *mut *mut c_void, + ) -> HRESULT, + + ReleaseMarshalData: unsafe extern "system" fn(*mut c_void, *mut c_void) -> HRESULT, + DisconnectObject: unsafe extern "system" fn(*mut c_void, u32) -> HRESULT, +} diff --git a/third_party/windows-winui/windows-core/src/imp/mod.rs b/third_party/windows-winui/windows-core/src/imp/mod.rs new file mode 100644 index 0000000000..d77baf13ab --- /dev/null +++ b/third_party/windows-winui/windows-core/src/imp/mod.rs @@ -0,0 +1,122 @@ +#[cfg(windows)] +include!("windows.rs"); + +mod agile; +mod bindings; +mod can_into; +mod com_bindings; +mod delegate_box; +mod ref_count; +mod sha1; +mod weak_ref_count; + +pub use agile::*; +pub(crate) use bindings::*; +pub use can_into::*; +pub use com_bindings::*; +pub use delegate_box::*; +pub use ref_count::*; +pub use sha1::*; +pub use weak_ref_count::*; + +/// Returns an opaque, process-specific encoding of a pointer suitable for use +/// as a delegate token. On Windows this calls `EncodePointer` so tokens cannot +/// be forged from raw pointer values; on non-Windows targets the raw pointer +/// value is returned as-is because there is no equivalent OS primitive and the +/// token is already opaque to callers and only meaningful within this process. +/// +/// The pointer is treated as an opaque value and is never dereferenced. +#[inline] +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn encode_pointer(ptr: *const core::ffi::c_void) -> i64 { + #[cfg(windows)] + unsafe { + EncodePointer(ptr) as i64 + } + #[cfg(not(windows))] + { + ptr as i64 + } +} + +#[doc(hidden)] +#[macro_export] +macro_rules! interface_hierarchy { + ($child:ident, $parent:ty) => { + impl ::windows_core::imp::CanInto<$parent> for $child {} + impl ::core::convert::From<&$child> for &$parent { + fn from(value: &$child) -> Self { + unsafe { ::core::mem::transmute(value) } + } + } + impl ::core::convert::From<$child> for $parent { + fn from(value: $child) -> Self { + unsafe { ::core::mem::transmute(value) } + } + } + }; + ($child:ident, $first:ty, $($rest:ty),+) => { + $crate::imp::interface_hierarchy!($child, $first); + $crate::imp::interface_hierarchy!($child, $($rest),+); + }; +} + +#[doc(hidden)] +pub use interface_hierarchy; + +#[doc(hidden)] +#[macro_export] +macro_rules! required_hierarchy { + ($child:ident, $parent:ty) => { + impl ::windows_core::imp::CanInto<$parent> for $child { const QUERY: bool = true; } + }; + ($child:ident, $first:ty, $($rest:ty),+) => { + $crate::imp::required_hierarchy!($child, $first); + $crate::imp::required_hierarchy!($child, $($rest),+); + }; +} + +#[doc(hidden)] +pub use required_hierarchy; + +#[doc(hidden)] +#[macro_export] +macro_rules! define_interface { + ($name:ident, $vtbl:ident, $iid:literal) => { + #[repr(transparent)] + #[derive(::core::cmp::PartialEq, ::core::cmp::Eq, ::core::clone::Clone)] + pub struct $name(::windows_core::IUnknown); + unsafe impl ::windows_core::Interface for $name { + type Vtable = $vtbl; + const IID: ::windows_core::GUID = ::windows_core::GUID::from_u128($iid); + } + impl ::core::fmt::Debug for $name { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> core::fmt::Result { + f.debug_tuple(stringify!($name)) + .field(&::windows_core::Interface::as_raw(self)) + .finish() + } + } + }; + ($name:ident, $vtbl:ident) => { + #[repr(transparent)] + #[derive(::core::cmp::PartialEq, ::core::cmp::Eq, ::core::clone::Clone)] + pub struct $name(::core::ptr::NonNull<::core::ffi::c_void>); + unsafe impl ::windows_core::Interface for $name { + type Vtable = $vtbl; + const IID: ::windows_core::GUID = ::windows_core::GUID::zeroed(); + const UNKNOWN: bool = false; + } + impl ::core::fmt::Debug for $name { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> core::fmt::Result { + f.debug_tuple(stringify!($name)).field(&self.0).finish() + } + } + }; +} + +#[doc(hidden)] +pub use define_interface; + +#[doc(hidden)] +pub use alloc::boxed::Box; diff --git a/third_party/windows-winui/windows-core/src/imp/ref_count.rs b/third_party/windows-winui/windows-core/src/imp/ref_count.rs new file mode 100644 index 0000000000..97e1f2a670 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/imp/ref_count.rs @@ -0,0 +1,33 @@ +use core::sync::atomic::{fence, AtomicI32, Ordering}; + +#[repr(transparent)] +#[derive(Default)] +pub struct RefCount(pub(crate) AtomicI32); + +impl RefCount { + /// Creates a new `RefCount` with an initial value of `1`. + pub const fn new(count: u32) -> Self { + Self(AtomicI32::new(count as i32)) + } + + /// Increments the reference count, returning the new value. + pub fn add_ref(&self) -> u32 { + (self.0.fetch_add(1, Ordering::Relaxed) + 1) as u32 + } + + /// Decrements the reference count, returning the new value. + /// + /// This operation inserts an `Acquire` fence when the reference count reaches zero. + /// This prevents reordering before the object is destroyed. + pub fn release(&self) -> u32 { + let remaining = self.0.fetch_sub(1, Ordering::Release) - 1; + + match remaining.cmp(&0) { + core::cmp::Ordering::Equal => fence(Ordering::Acquire), + core::cmp::Ordering::Less => panic!("Object has been over-released."), + core::cmp::Ordering::Greater => {} + } + + remaining as u32 + } +} diff --git a/third_party/windows-winui/windows-core/src/imp/sha1.rs b/third_party/windows-winui/windows-core/src/imp/sha1.rs new file mode 100644 index 0000000000..88f80ca924 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/imp/sha1.rs @@ -0,0 +1,487 @@ +pub const fn sha1(data: &ConstBuffer) -> Digest { + let state: [u32; 5] = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0]; + let len: u64 = 0; + let blocks = Blocks { + len: 0, + data: [0; 64], + }; + let (blocks, len, state) = process_blocks(blocks, data, len, state); + digest(state, len, blocks) +} + +const BUFFER_SIZE: usize = 1024; + +pub struct ConstBuffer { + data: [u8; BUFFER_SIZE], + head: usize, +} + +impl Default for ConstBuffer { + fn default() -> Self { + Self::new() + } +} + +impl ConstBuffer { + pub const fn for_class() -> Self { + Self::new() + .push_slice(b"rc(") + .push_slice(C::NAME.as_bytes()) + .push(b';') + .push_other(I::SIGNATURE) + .push(b')') + } + + pub const fn for_interface() -> Self { + Self::new().push_guid(&T::IID) + } + + pub const fn from_slice(slice: &[u8]) -> Self { + let s = Self::new(); + s.push_slice(slice) + } + + pub const fn new() -> Self { + Self { + data: [0; BUFFER_SIZE], + head: 0, + } + } + + pub const fn push_slice(self, slice: &[u8]) -> Self { + self.push_amount(slice, slice.len()) + } + + const fn get(&self, index: usize) -> u8 { + self.data[index] + } + + const fn len(&self) -> usize { + self.head + } + + pub fn as_slice(&self) -> &[u8] { + &self.data[..self.head] + } + + pub const fn push_other(self, other: Self) -> Self { + self.push_amount(&other.data, other.len()) + } + + const fn push(mut self, value: u8) -> Self { + self.data[self.head] = value; + self.head += 1; + self + } + + const fn push_hex_u8(self, value: u8) -> Self { + const fn digit(mut value: u8) -> u8 { + value &= 0xF; + + if value < 10 { + b'0' + value + } else { + b'a' + (value - 10) + } + } + + self.push(digit(value >> 4)).push(digit(value)) + } + + const fn push_hex_u16(self, value: u16) -> Self { + self.push_hex_u8((value >> 8) as u8) + .push_hex_u8((value & 0xFF) as u8) + } + + const fn push_hex_u32(self, value: u32) -> Self { + self.push_hex_u16((value >> 16) as u16) + .push_hex_u16((value & 0xFFFF) as u16) + } + + const fn push_amount(mut self, slice: &[u8], amount: usize) -> Self { + let mut i = 0; + while i < amount { + self.data[self.head + i] = slice[i]; + i += 1; + } + self.head += i; + self + } + + const fn push_guid(self, guid: &crate::GUID) -> Self { + self.push(b'{') + .push_hex_u32(guid.data1) + .push(b'-') + .push_hex_u16(guid.data2) + .push(b'-') + .push_hex_u16(guid.data3) + .push(b'-') + .push_hex_u16(((guid.data4[0] as u16) << 8) | guid.data4[1] as u16) + .push(b'-') + .push_hex_u16(((guid.data4[2] as u16) << 8) | guid.data4[3] as u16) + .push_hex_u16(((guid.data4[4] as u16) << 8) | guid.data4[5] as u16) + .push_hex_u16(((guid.data4[6] as u16) << 8) | guid.data4[7] as u16) + .push(b'}') + } +} + +struct Blocks { + len: u32, + data: [u8; 64], +} + +const fn process_blocks( + mut blocks: Blocks, + data: &ConstBuffer, + mut len: u64, + mut state: [u32; 5], +) -> (Blocks, u64, [u32; 5]) { + const fn as_block(input: &ConstBuffer, offset: usize) -> [u32; 16] { + let mut result = [0u32; 16]; + + let mut i = 0; + while i != 16 { + let off = offset + (i * 4); + result[i] = (input.get(off + 3) as u32) + | ((input.get(off + 2) as u32) << 8) + | ((input.get(off + 1) as u32) << 16) + | ((input.get(off) as u32) << 24); + i += 1; + } + result + } + + const fn clone_from_slice_64( + mut data: [u8; 64], + slice: &[u8], + offset: usize, + num_elems: usize, + ) -> [u8; 64] { + let mut i = 0; + while i < num_elems { + data[i] = slice[offset + i]; + i += 1; + } + data + } + + let mut i = 0; + while i < data.len() { + if data.len() - i >= 64 { + let chunk_block = as_block(data, i); + len += 64; + state = process_state(state, chunk_block); + i += 64; + } else { + let num_elems = data.len() - i; + blocks.data = clone_from_slice_64(blocks.data, &data.data, i, num_elems); + blocks.len = num_elems as u32; + break; + } + } + (blocks, len, state) +} + +const fn process_state(mut state: [u32; 5], block: [u32; 16]) -> [u32; 5] { + let a = state[0]; + let b = state[1]; + let c = state[2]; + let d = state[3]; + let e = state[4]; + let (block, b, e) = r0(block, a, b, c, d, e, 0); + let (block, a, d) = r0(block, e, a, b, c, d, 1); + let (block, e, c) = r0(block, d, e, a, b, c, 2); + let (block, d, b) = r0(block, c, d, e, a, b, 3); + let (block, c, a) = r0(block, b, c, d, e, a, 4); + let (block, b, e) = r0(block, a, b, c, d, e, 5); + let (block, a, d) = r0(block, e, a, b, c, d, 6); + let (block, e, c) = r0(block, d, e, a, b, c, 7); + let (block, d, b) = r0(block, c, d, e, a, b, 8); + let (block, c, a) = r0(block, b, c, d, e, a, 9); + let (block, b, e) = r0(block, a, b, c, d, e, 10); + let (block, a, d) = r0(block, e, a, b, c, d, 11); + let (block, e, c) = r0(block, d, e, a, b, c, 12); + let (block, d, b) = r0(block, c, d, e, a, b, 13); + let (block, c, a) = r0(block, b, c, d, e, a, 14); + let (block, b, e) = r0(block, a, b, c, d, e, 15); + let (block, a, d) = r1(block, e, a, b, c, d, 0); + let (block, e, c) = r1(block, d, e, a, b, c, 1); + let (block, d, b) = r1(block, c, d, e, a, b, 2); + let (block, c, a) = r1(block, b, c, d, e, a, 3); + let (block, b, e) = r2(block, a, b, c, d, e, 4); + let (block, a, d) = r2(block, e, a, b, c, d, 5); + let (block, e, c) = r2(block, d, e, a, b, c, 6); + let (block, d, b) = r2(block, c, d, e, a, b, 7); + let (block, c, a) = r2(block, b, c, d, e, a, 8); + let (block, b, e) = r2(block, a, b, c, d, e, 9); + let (block, a, d) = r2(block, e, a, b, c, d, 10); + let (block, e, c) = r2(block, d, e, a, b, c, 11); + let (block, d, b) = r2(block, c, d, e, a, b, 12); + let (block, c, a) = r2(block, b, c, d, e, a, 13); + let (block, b, e) = r2(block, a, b, c, d, e, 14); + let (block, a, d) = r2(block, e, a, b, c, d, 15); + let (block, e, c) = r2(block, d, e, a, b, c, 0); + let (block, d, b) = r2(block, c, d, e, a, b, 1); + let (block, c, a) = r2(block, b, c, d, e, a, 2); + let (block, b, e) = r2(block, a, b, c, d, e, 3); + let (block, a, d) = r2(block, e, a, b, c, d, 4); + let (block, e, c) = r2(block, d, e, a, b, c, 5); + let (block, d, b) = r2(block, c, d, e, a, b, 6); + let (block, c, a) = r2(block, b, c, d, e, a, 7); + let (block, b, e) = r3(block, a, b, c, d, e, 8); + let (block, a, d) = r3(block, e, a, b, c, d, 9); + let (block, e, c) = r3(block, d, e, a, b, c, 10); + let (block, d, b) = r3(block, c, d, e, a, b, 11); + let (block, c, a) = r3(block, b, c, d, e, a, 12); + let (block, b, e) = r3(block, a, b, c, d, e, 13); + let (block, a, d) = r3(block, e, a, b, c, d, 14); + let (block, e, c) = r3(block, d, e, a, b, c, 15); + let (block, d, b) = r3(block, c, d, e, a, b, 0); + let (block, c, a) = r3(block, b, c, d, e, a, 1); + let (block, b, e) = r3(block, a, b, c, d, e, 2); + let (block, a, d) = r3(block, e, a, b, c, d, 3); + let (block, e, c) = r3(block, d, e, a, b, c, 4); + let (block, d, b) = r3(block, c, d, e, a, b, 5); + let (block, c, a) = r3(block, b, c, d, e, a, 6); + let (block, b, e) = r3(block, a, b, c, d, e, 7); + let (block, a, d) = r3(block, e, a, b, c, d, 8); + let (block, e, c) = r3(block, d, e, a, b, c, 9); + let (block, d, b) = r3(block, c, d, e, a, b, 10); + let (block, c, a) = r3(block, b, c, d, e, a, 11); + let (block, b, e) = r4(block, a, b, c, d, e, 12); + let (block, a, d) = r4(block, e, a, b, c, d, 13); + let (block, e, c) = r4(block, d, e, a, b, c, 14); + let (block, d, b) = r4(block, c, d, e, a, b, 15); + let (block, c, a) = r4(block, b, c, d, e, a, 0); + let (block, b, e) = r4(block, a, b, c, d, e, 1); + let (block, a, d) = r4(block, e, a, b, c, d, 2); + let (block, e, c) = r4(block, d, e, a, b, c, 3); + let (block, d, b) = r4(block, c, d, e, a, b, 4); + let (block, c, a) = r4(block, b, c, d, e, a, 5); + let (block, b, e) = r4(block, a, b, c, d, e, 6); + let (block, a, d) = r4(block, e, a, b, c, d, 7); + let (block, e, c) = r4(block, d, e, a, b, c, 8); + let (block, d, b) = r4(block, c, d, e, a, b, 9); + let (block, c, a) = r4(block, b, c, d, e, a, 10); + let (block, b, e) = r4(block, a, b, c, d, e, 11); + let (block, a, d) = r4(block, e, a, b, c, d, 12); + let (block, e, c) = r4(block, d, e, a, b, c, 13); + let (block, d, b) = r4(block, c, d, e, a, b, 14); + let (_, c, a) = r4(block, b, c, d, e, a, 15); + + state[0] = state[0].wrapping_add(a); + state[1] = state[1].wrapping_add(b); + state[2] = state[2].wrapping_add(c); + state[3] = state[3].wrapping_add(d); + state[4] = state[4].wrapping_add(e); + state +} + +const fn digest(mut state: [u32; 5], len: u64, blocks: Blocks) -> Digest { + const fn clone_from_slice_128( + mut data: [u8; 128], + slice: &[u8], + offset: usize, + num_elems: usize, + ) -> [u8; 128] { + let mut i = 0; + while i < num_elems { + data[i] = slice[offset + i]; + i += 1; + } + data + } + + const fn clone_slice_128(mut data: [u8; 128], slice: &[u8], _offset: usize) -> [u8; 128] { + let mut i = 0; + while i < slice.len() { + data[_offset + i] = slice[i]; + i += 1; + } + data + } + + const fn as_block(input: &[u8], offset: usize) -> [u32; 16] { + let mut result = [0u32; 16]; + + let mut i = 0; + while i != 16 { + let off = offset + (i * 4); + result[i] = (input[off + 3] as u32) + | ((input[off + 2] as u32) << 8) + | ((input[off + 1] as u32) << 16) + | ((input[off] as u32) << 24); + i += 1; + } + result + } + + let bits = (len + (blocks.len as u64)) * 8; + let extra = [ + (bits >> 56) as u8, + (bits >> 48) as u8, + (bits >> 40) as u8, + (bits >> 32) as u8, + (bits >> 24) as u8, + (bits >> 16) as u8, + (bits >> 8) as u8, + bits as u8, + ]; + let mut last = [0; 128]; + let blocklen = blocks.len as usize; + last = clone_from_slice_128(last, &blocks.data, 0, blocklen); + last[blocklen] = 0x80; + + if blocklen < 56 { + last = clone_slice_128(last, &extra, 56); + state = process_state(state, as_block(&last, 0)); + } else { + last = clone_slice_128(last, &extra, 120); + state = process_state(state, as_block(&last, 0)); + state = process_state(state, as_block(&last, 64)); + } + Digest { data: state } +} + +const fn rol(value: u32, bits: u32) -> u32 { + value.rotate_left(bits) +} + +const fn blk(block: &[u32], i: usize) -> u32 { + let value = block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ block[(i + 2) & 15] ^ block[i]; + rol(value, 1) +} + +const fn r0( + block: [u32; 16], + v: u32, + mut w: u32, + x: u32, + y: u32, + mut z: u32, + i: usize, +) -> ([u32; 16], u32, u32) { + let n = ((w & (x ^ y)) ^ y) + .wrapping_add(block[i]) + .wrapping_add(0x5a82_7999) + .wrapping_add(rol(v, 5)); + z = z.wrapping_add(n); + w = rol(w, 30); + (block, w, z) +} + +const fn r1( + mut block: [u32; 16], + v: u32, + mut w: u32, + x: u32, + y: u32, + mut z: u32, + i: usize, +) -> ([u32; 16], u32, u32) { + block[i] = blk(&block, i); + let n = ((w & (x ^ y)) ^ y) + .wrapping_add(block[i]) + .wrapping_add(0x5a82_7999) + .wrapping_add(rol(v, 5)); + z = z.wrapping_add(n); + w = rol(w, 30); + (block, w, z) +} + +const fn r2( + mut block: [u32; 16], + v: u32, + mut w: u32, + x: u32, + y: u32, + mut z: u32, + i: usize, +) -> ([u32; 16], u32, u32) { + block[i] = blk(&block, i); + let n = (w ^ x ^ y) + .wrapping_add(block[i]) + .wrapping_add(0x6ed9_eba1) + .wrapping_add(rol(v, 5)); + z = z.wrapping_add(n); + w = rol(w, 30); + (block, w, z) +} + +const fn r3( + mut block: [u32; 16], + v: u32, + mut w: u32, + x: u32, + y: u32, + mut z: u32, + i: usize, +) -> ([u32; 16], u32, u32) { + block[i] = blk(&block, i); + let n = (((w | x) & y) | (w & x)) + .wrapping_add(block[i]) + .wrapping_add(0x8f1b_bcdc) + .wrapping_add(rol(v, 5)); + z = z.wrapping_add(n); + w = rol(w, 30); + (block, w, z) +} + +const fn r4( + mut block: [u32; 16], + v: u32, + mut w: u32, + x: u32, + y: u32, + mut z: u32, + i: usize, +) -> ([u32; 16], u32, u32) { + block[i] = blk(&block, i); + let n = (w ^ x ^ y) + .wrapping_add(block[i]) + .wrapping_add(0xca62_c1d6) + .wrapping_add(rol(v, 5)); + z = z.wrapping_add(n); + w = rol(w, 30); + (block, w, z) +} + +pub struct Digest { + data: [u32; 5], +} + +impl Digest { + pub const fn bytes(&self) -> [u8; 20] { + [ + (self.data[0] >> 24) as u8, + (self.data[0] >> 16) as u8, + (self.data[0] >> 8) as u8, + self.data[0] as u8, + (self.data[1] >> 24) as u8, + (self.data[1] >> 16) as u8, + (self.data[1] >> 8) as u8, + self.data[1] as u8, + (self.data[2] >> 24) as u8, + (self.data[2] >> 16) as u8, + (self.data[2] >> 8) as u8, + self.data[2] as u8, + (self.data[3] >> 24) as u8, + (self.data[3] >> 16) as u8, + (self.data[3] >> 8) as u8, + self.data[3] as u8, + (self.data[4] >> 24) as u8, + (self.data[4] >> 16) as u8, + (self.data[4] >> 8) as u8, + self.data[4] as u8, + ] + } +} + +impl core::fmt::Display for Digest { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + for i in &self.data { + write!(f, "{i:08x}")?; + } + Ok(()) + } +} diff --git a/third_party/windows-winui/windows-core/src/imp/weak_ref_count.rs b/third_party/windows-winui/windows-core/src/imp/weak_ref_count.rs new file mode 100644 index 0000000000..8fda2849ae --- /dev/null +++ b/third_party/windows-winui/windows-core/src/imp/weak_ref_count.rs @@ -0,0 +1,329 @@ +use super::*; +use crate::{IUnknown, IUnknown_Vtbl, Interface, GUID, HRESULT}; +use core::ffi::c_void; +use core::mem::{transmute, transmute_copy}; +use core::ptr::null_mut; +use core::sync::atomic::{AtomicIsize, Ordering}; + +#[repr(transparent)] +#[derive(Default)] +pub struct WeakRefCount(AtomicIsize); + +impl WeakRefCount { + pub const fn new() -> Self { + Self(AtomicIsize::new(1)) + } + + pub fn add_ref(&self) -> u32 { + self.0 + .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |count_or_pointer| { + bool::then_some(!is_weak_ref(count_or_pointer), count_or_pointer + 1) + }) + .map_or_else( + |pointer| unsafe { TearOff::decode(pointer).strong_count.add_ref() }, + |u| u as u32 + 1, + ) + } + + #[inline(always)] + pub fn is_one(&self) -> bool { + self.0.load(Ordering::Acquire) == 1 + } + + pub fn release(&self) -> u32 { + self.0 + .fetch_update(Ordering::Release, Ordering::Relaxed, |count_or_pointer| { + bool::then_some(!is_weak_ref(count_or_pointer), count_or_pointer - 1) + }) + .map_or_else( + |pointer| unsafe { + let tear_off = TearOff::decode(pointer); + let remaining = tear_off.strong_count.release(); + + // If this is the last strong reference, we can release the weak reference implied by the strong reference. + // There may still be weak references, so the WeakRelease is called to handle such possibilities. + if remaining == 0 { + TearOff::WeakRelease(&mut tear_off.weak_vtable as *mut _ as _); + } + + remaining + }, + |u| u as u32 - 1, + ) + } + + /// # Safety + pub unsafe fn query(&self, iid: &GUID, object: *mut c_void) -> *mut c_void { + unsafe { + if iid != &IWeakReferenceSource::IID { + return null_mut(); + } + + let mut count_or_pointer = self.0.load(Ordering::Relaxed); + + if is_weak_ref(count_or_pointer) { + return TearOff::from_encoding(count_or_pointer); + } + + let tear_off = TearOff::new(object, count_or_pointer as u32); + let tear_off_ptr: *mut c_void = transmute_copy(&tear_off); + let encoding: usize = ((tear_off_ptr as usize) >> 1) | (1 << (usize::BITS - 1)); + + loop { + match self.0.compare_exchange_weak( + count_or_pointer, + encoding as isize, + Ordering::AcqRel, + Ordering::Relaxed, + ) { + Ok(_) => { + let result: *mut c_void = transmute(tear_off); + TearOff::from_strong_ptr(result).strong_count.add_ref(); + return result; + } + Err(pointer) => count_or_pointer = pointer, + } + + if is_weak_ref(count_or_pointer) { + return TearOff::from_encoding(count_or_pointer); + } + + TearOff::from_strong_ptr(tear_off_ptr) + .strong_count + .0 + .store(count_or_pointer as i32, Ordering::SeqCst); + } + } + } +} + +fn is_weak_ref(value: isize) -> bool { + value < 0 +} + +#[repr(C)] +struct TearOff { + strong_vtable: *const IWeakReferenceSource_Vtbl, + weak_vtable: *const IWeakReference_Vtbl, + object: *mut c_void, + strong_count: RefCount, + weak_count: RefCount, +} + +impl TearOff { + #[expect(clippy::new_ret_no_self)] + unsafe fn new(object: *mut c_void, strong_count: u32) -> IWeakReferenceSource { + unsafe { + transmute(Box::new(Self { + strong_vtable: &Self::STRONG_VTABLE, + weak_vtable: &Self::WEAK_VTABLE, + object, + strong_count: RefCount::new(strong_count), + weak_count: RefCount::new(1), + })) + } + } + + unsafe fn from_encoding(encoding: isize) -> *mut c_void { + unsafe { + let tear_off = Self::decode(encoding); + tear_off.strong_count.add_ref(); + tear_off as *mut _ as *mut _ + } + } + + const STRONG_VTABLE: IWeakReferenceSource_Vtbl = IWeakReferenceSource_Vtbl { + base__: IUnknown_Vtbl { + QueryInterface: Self::StrongQueryInterface, + AddRef: Self::StrongAddRef, + Release: Self::StrongRelease, + }, + GetWeakReference: Self::StrongDowngrade, + }; + + const WEAK_VTABLE: IWeakReference_Vtbl = IWeakReference_Vtbl { + base__: IUnknown_Vtbl { + QueryInterface: Self::WeakQueryInterface, + AddRef: Self::WeakAddRef, + Release: Self::WeakRelease, + }, + Resolve: Self::WeakUpgrade, + }; + + unsafe fn from_strong_ptr<'a>(this: *mut c_void) -> &'a mut Self { + unsafe { &mut *(this as *mut *mut c_void as *mut Self) } + } + + unsafe fn from_weak_ptr<'a>(this: *mut c_void) -> &'a mut Self { + unsafe { &mut *((this as *mut *mut c_void).sub(1) as *mut Self) } + } + + unsafe fn decode<'a>(value: isize) -> &'a mut Self { + unsafe { &mut *((value << 1) as *mut Self) as &mut Self } + } + + unsafe fn query_interface(&self, iid: *const GUID, interface: *mut *mut c_void) -> HRESULT { + unsafe { + ((*(*(self.object as *mut *mut IUnknown_Vtbl))).QueryInterface)( + self.object, + iid, + interface, + ) + } + } + + unsafe extern "system" fn StrongQueryInterface( + ptr: *mut c_void, + iid: *const GUID, + interface: *mut *mut c_void, + ) -> HRESULT { + unsafe { + let this = Self::from_strong_ptr(ptr); + + if iid.is_null() || interface.is_null() { + return E_POINTER; + } + + // Only directly respond to queries for the the tear-off's strong interface. This is + // effectively a self-query. + if *iid == IWeakReferenceSource::IID { + *interface = ptr; + this.strong_count.add_ref(); + return HRESULT(0); + } + + // As the tear-off is sharing the identity of the object, simply delegate any remaining + // queries to the object. + this.query_interface(iid, interface) + } + } + + unsafe extern "system" fn WeakQueryInterface( + ptr: *mut c_void, + iid: *const GUID, + interface: *mut *mut c_void, + ) -> HRESULT { + unsafe { + let this = Self::from_weak_ptr(ptr); + + if iid.is_null() || interface.is_null() { + return E_POINTER; + } + + // While the weak vtable is packed into the same allocation as the strong vtable and + // tear-off, it represents a distinct COM identity and thus does not share or delegate to + // the object. + + *interface = if *iid == IWeakReference::IID + || *iid == IUnknown::IID + || *iid == IAgileObject::IID + { + ptr + } else { + #[cfg(windows)] + if *iid == IMarshal::IID { + this.weak_count.add_ref(); + return marshaler(transmute::<*mut c_void, IUnknown>(ptr), interface); + } + + null_mut() + }; + + if (*interface).is_null() { + E_NOINTERFACE + } else { + this.weak_count.add_ref(); + HRESULT(0) + } + } + } + + unsafe extern "system" fn StrongAddRef(ptr: *mut c_void) -> u32 { + unsafe { + let this = Self::from_strong_ptr(ptr); + + // Implement `AddRef` directly as we own the strong reference. + this.strong_count.add_ref() + } + } + + unsafe extern "system" fn WeakAddRef(ptr: *mut c_void) -> u32 { + unsafe { + let this = Self::from_weak_ptr(ptr); + + // Implement `AddRef` directly as we own the weak reference. + this.weak_count.add_ref() + } + } + + unsafe extern "system" fn StrongRelease(ptr: *mut c_void) -> u32 { + unsafe { + let this = Self::from_strong_ptr(ptr); + + // Forward strong `Release` to the object so that it can destroy itself. It will then + // decrement its weak reference and allow the tear-off to be released as needed. + ((*(*(this.object as *mut *mut IUnknown_Vtbl))).Release)(this.object) + } + } + + unsafe extern "system" fn WeakRelease(ptr: *mut c_void) -> u32 { + unsafe { + let this = Self::from_weak_ptr(ptr); + + // Implement `Release` directly as we own the weak reference. + let remaining = this.weak_count.release(); + + // If there are no remaining references, it means that the object has already been + // destroyed. Go ahead and destroy the tear-off. + if remaining == 0 { + let _ = Box::from_raw(this); + } + + remaining + } + } + + unsafe extern "system" fn StrongDowngrade( + ptr: *mut c_void, + interface: *mut *mut c_void, + ) -> HRESULT { + unsafe { + let this = Self::from_strong_ptr(ptr); + + // The strong vtable hands out a reference to the weak vtable. This is always safe and + // straightforward since a strong reference guarantees there is at least one weak + // reference. + *interface = &mut this.weak_vtable as *mut _ as _; + this.weak_count.add_ref(); + HRESULT(0) + } + } + + unsafe extern "system" fn WeakUpgrade( + ptr: *mut c_void, + iid: *const GUID, + interface: *mut *mut c_void, + ) -> HRESULT { + unsafe { + let this = Self::from_weak_ptr(ptr); + + this.strong_count + .0 + .fetch_update(Ordering::Acquire, Ordering::Relaxed, |count| { + // Stabilize the object for the duration of the `QueryInterface` call. + bool::then_some(count != 0, count + 1) + }) + .map_or_else( + |_| { + *interface = null_mut(); + HRESULT(0) + }, + |_| { + let result = this.query_interface(iid, interface); + this.strong_count.0.fetch_sub(1, Ordering::Relaxed); + result + }, + ) + } + } +} diff --git a/third_party/windows-winui/windows-core/src/imp/windows.rs b/third_party/windows-winui/windows-core/src/imp/windows.rs new file mode 100644 index 0000000000..d9f44ad341 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/imp/windows.rs @@ -0,0 +1,11 @@ +mod factory_cache; +pub use factory_cache::*; + +mod generic_factory; +pub use generic_factory::*; + +mod marshaler; +pub use marshaler::*; + +mod array_proxy; +pub use array_proxy::*; diff --git a/third_party/windows-winui/windows-core/src/implement_macro.rs b/third_party/windows-winui/windows-core/src/implement_macro.rs new file mode 100644 index 0000000000..36aae162f8 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/implement_macro.rs @@ -0,0 +1,1208 @@ +//! A `macro_rules!` declarative alternative to the `#[implement]` proc-macro. +//! +//! `#[implement]` is the canonical way to wire up a Rust type as the implementer of one or +//! more COM interfaces, but it pulls in `syn`, `quote`, and `proc-macro2`. Consumers who +//! disable the `proc-macros` default feature on `windows-core` still need a way to do this; +//! [`implement_decl!`] fills that role. +//! +//! ## Scope +//! +//! `implement_decl!` targets the dominant hand-written case: an **always-agile** Rust type +//! (generic or non-generic) that implements **one or more** COM interfaces (each declared +//! either by [`crate::interface_decl!`] or by `#[interface]`). It does not support: +//! +//! - per-instance `trust_level` configuration (always `0` / Base), +//! - per-instance opt-out of agility, +//! - per-instance opt-out of dynamic casting. +//! +//! It **does** support COM aggregation (a `Compose` impl is emitted unconditionally), the +//! IMarshal tear-off, the dynamic-cast pseudo-IID, and the agile-object IID. +//! +//! ## Syntax +//! +//! ```rust,ignore +//! use windows_core::*; +//! +//! interface_decl! { +//! pub unsafe trait IFoo(IFoo_Vtbl, IFoo_Impl) : IUnknown +//! = 0x094d70d6_5202_44b8_abb8_43860da5aca2 +//! { +//! unsafe fn Method(&self) -> HRESULT; +//! } +//! } +//! +//! pub struct Foo; +//! +//! implement_decl! { +//! impl Foo as pub Foo_Impl: [IFoo] +//! } +//! +//! impl IFoo_Impl for Foo_Impl { +//! unsafe fn Method(&self) -> HRESULT { HRESULT(0) } +//! } +//! ``` +//! +//! - `Foo` is the user-defined implementer type — declared **separately**, before invoking +//! the macro. +//! - `Foo_Impl` is the wrapper that the macro defines. Visibility (`pub`, `pub(crate)`, …) +//! may be supplied before the ident; it defaults to private. +//! - Each list entry is just the interface ident. The associated `_Vtbl` type is reached +//! through `::Vtable`, so it does not need to be spelled out, and +//! the `_Impl` trait is referenced only by user code outside the macro. +//! - At least one interface must be supplied. +//! +//! ## Generic implementer types +//! +//! For generic implementers like `StockIterable` that implement generic interfaces such +//! as `IIterable`, the macro accepts a leading `` generic-parameter list and a +//! mandatory trailing `where` clause. Each interface entry is then spelled out as a full +//! type rather than a bare ident: +//! +//! ```rust,ignore +//! implement_decl! { +//! impl StockIterable as pub(crate) StockIterable_Impl: [ +//! IIterable, +//! ] +//! where T: RuntimeType + 'static, T::Default: Clone +//! } +//! ``` +//! +//! The `where` clause is forwarded verbatim to every emitted impl and to the `Foo_Impl` +//! struct definition. +//! +//! Generic invocations differ from non-generic ones in two emission details: +//! +//! - per-interface vtables are stored as **associated constants** on `impl Foo_Impl` +//! (an in-`fn` `const C: T = …;` cannot reference outer generic parameters; an associated +//! constant can), +//! - `into_outer` is *not* `const fn` (a generic `fn` cannot be `const fn` while reading +//! associated constants whose values depend on `Self`'s type arguments), and there is no +//! `into_static` (a generic type has no fixed layout for static storage). +//! +//! ## Generated items +//! +//! - `struct Foo_Impl` — `#[repr(C)]` with leading `base: ComposeBase`, `identity: +//! &'static IInspectable_Vtbl`, one `&'static IFace_Vtbl` field per interface (named +//! after the interface ident), then `this: Foo`, `count: WeakRefCount`. +//! - `impl Foo { fn into_outer, fn into_static }` — the same shape as the proc-macro. +//! Vtables are stored as const-promoted `&'static` references to `const fn` results; +//! this avoids needing to synthesize per-interface const identifiers. +//! - `impl Deref for Foo_Impl`. +//! - `impl IUnknownImpl for Foo_Impl` with a `QueryInterface` that handles, in order: +//! - `IUnknown` / `IInspectable` / `IAgileObject` → identity vtable, +//! - each declared interface IID → that interface's vtable, +//! - `IMarshal` (Windows only) → standard marshaler, +//! - `DYNAMIC_CAST_IID` → `&dyn Any` write, +//! - weak-reference tear-off, +//! - aggregation fall-through to the inner non-delegating `IInspectable`. +//! - `impl ComObjectInner for Foo`. +//! - `impl Compose for Foo` so the type can participate in WinRT aggregation. +//! - `impl From` for `IUnknown`, `IInspectable`, and each declared interface. +//! - `impl ComObjectInterface for Foo_Impl` for `IUnknown`, `IInspectable`, and each +//! declared interface. +//! - `impl AsImpl for I` for each declared interface. + +/// Declares a Rust type as the COM implementer of one or more interfaces, without using +/// the `#[implement]` proc-macro. See the module-level documentation for the supported +/// syntax and scope. +#[macro_export] +macro_rules! implement_decl { + // Generic form: `impl Name as Vis Name_Impl : [Iface<…>, …] where …`. + // + // Listed before the non-generic arm so that a leading `<` reliably steers here. + // The `where` clause is required and is forwarded verbatim to every emitted impl + // and to the `Foo_Impl` struct definition. + ( + impl < $($gp:ident),+ $(,)? > + $name:ident as $impl_vis:vis $impl_name:ident + : [ + $( $ifty:ty ),+ $(,)? + ] + where $($wc:tt)+ + ) => { + $crate::__implement_decl_g_zip! { + @zip + ctx: { + generics: [ $($gp),+ ], + wc: { $($wc)+ }, + vis: $impl_vis, + name: $name, + impl_name: $impl_name, + }, + names: [ + __iface0 __iface1 __iface2 __iface3 + __iface4 __iface5 __iface6 __iface7 + __iface8 __iface9 __iface10 __iface11 + __iface12 __iface13 __iface14 __iface15 + ], + tys: [ $($ifty),+ ], + acc: [ ] + } + }; + + // Non-generic form: `impl Name as Vis Name_Impl : [Iface, …]`. + ( + impl $name:ident as $impl_vis:vis $impl_name:ident : [ + $( $iface:ident ),+ $(,)? + ] $(,)? + ) => { + // The vtable type for each interface is resolved via the `Interface::Vtable` + // associated type (i.e. `::Vtable`), so the caller does not + // have to spell out `IFoo_Vtbl`. `macro_rules!` cannot synthesize identifiers, + // but every use of the vtable here is as a *type*, so the associated-type path + // is a drop-in substitute for the concrete `_Vtbl` ident. Inherent items on the + // concrete vtable (`new`, `matches`) remain reachable through the path. + // + // The `_Impl` trait is *only* referenced by user code (e.g. `impl IFoo_Impl for + // Foo_Impl { ... }`), never by this macro, so nothing needs to be inferred for it. + + // The first declared interface doubles as the `Name` type argument to + // `IInspectable_Vtbl::new`, mirroring the proc-macro so that + // `GetRuntimeClassName` works for runtime-class implementers. + $crate::__implement_decl_first_iface! { + @find + args: [ vis: $impl_vis, name: $name, impl_name: $impl_name, ], + remaining: [ $( ($iface) )+ ] + } + + // Per-interface impls. Each of these is a sequence of `impl` items, which the + // helper macro emits at item position by recursion. + $crate::__implement_decl_per_iface_impls!( + $name, $impl_name, + $( ($iface), )+ + ); + }; +} + +// --- Entry point: extract the first interface and forward to the main accumulator. +// +// `IInspectable_Vtbl::new::<_, Name, _>()` needs a `Name: RuntimeName` type argument. We +// pick the first declared interface (matching the proc-macro behavior); a separate match +// arm captures it before the main accumulator runs. + +#[doc(hidden)] +#[macro_export] +macro_rules! __implement_decl_first_iface { + (@find + args: [ vis: $impl_vis:vis, name: $name:ident, impl_name: $impl_name:ident, ], + remaining: [ ($first_iface:ident) $($rest:tt)* ] + ) => { + $crate::__implement_decl_struct! { + @walk + vis: $impl_vis, + name: $name, + impl_name: $impl_name, + first_iface: $first_iface, + fields: { }, + inits: { }, + qi_pairs: [ ], + offset: [ () () ], // 2 unary-counted placeholders for the -2 starting offset + remaining: [ ($first_iface) $($rest)* ] + } + }; +} + +// --- Main accumulator: walks the interface list, accumulating struct fields, the +// `into_outer` initializer list, and `(iface, vtbl)` pairs for the `QueryInterface` body. +// +// **Important — macro hygiene.** The accumulator carries only *data* tokens (idents, +// types) so that all references to `self`, `iid`, and the `'found` label end up emitted +// from a single macro invocation (the base arm). If those references were generated in +// the recursive arm and shipped across invocations via a metavariable, each invocation +// would attach a fresh expansion-site hygiene context to them, and the base arm's +// `fn QueryInterface(&self, iid: ..., …) { 'found: { ... } }` declarations would not +// unify with the references in the spliced tokens. +// +// The `offset:` token list is a unary counter (`()` per pointer-slot of offset). Each +// interface chain advances `offset` by one slot; we convert the unary count to the +// `OFFSET` const generic at emission time using `__implement_decl_offset_negate!`. + +#[doc(hidden)] +#[macro_export] +macro_rules! __implement_decl_struct { + // One more interface to consume. + (@walk + vis: $impl_vis:vis, + name: $name:ident, + impl_name: $impl_name:ident, + first_iface: $first_iface:ident, + fields: { $($fields:tt)* }, + inits: { $($inits:tt)* }, + qi_pairs: [ $($qi_pairs:tt)* ], + offset: [ $($offset:tt)* ], + remaining: [ ($iface:ident) $($rest:tt)* ] + ) => { + $crate::__implement_decl_struct! { + @walk + vis: $impl_vis, + name: $name, + impl_name: $impl_name, + first_iface: $first_iface, + fields: { + $($fields)* + #[allow(non_snake_case)] + pub $iface: &'static <$iface as $crate::Interface>::Vtable, + }, + inits: { + $($inits)* + $iface: { + const C: <$iface as $crate::Interface>::Vtable = + <<$iface as $crate::Interface>::Vtable>::new::< + $impl_name, + { $crate::__implement_decl_offset_negate!($($offset)*) }, + >(); + &C + }, + }, + qi_pairs: [ $($qi_pairs)* ($iface) ], + offset: [ $($offset)* () ], + remaining: [ $($rest)* ] + } + }; + + // No more interfaces: emit struct + IUnknownImpl + Deref + ComObjectInner + Compose + + // From for IUnknown/IInspectable + ComObjectInterface for IUnknown/IInspectable. + (@walk + vis: $impl_vis:vis, + name: $name:ident, + impl_name: $impl_name:ident, + first_iface: $first_iface:ident, + fields: { $($fields:tt)* }, + inits: { $($inits:tt)* }, + qi_pairs: [ $(($qi_iface:ident))* ], + offset: [ $($offset:tt)* ], + remaining: [ ] + ) => { + #[repr(C)] + #[allow(non_camel_case_types, non_snake_case)] + $impl_vis struct $impl_name { + pub base: $crate::ComposeBase, + pub identity: &'static $crate::IInspectable_Vtbl, + $($fields)* + pub this: $name, + pub count: $crate::imp::WeakRefCount, + } + + impl $name { + /// Constructs the outer (boxed) representation of this implementer. + /// + /// This is an implementation detail; user code should normally go through + /// [`ComObject::new`](::windows_core::ComObject::new) instead. + #[doc(hidden)] + #[inline(always)] + #[allow(non_snake_case)] + pub const fn into_outer(self) -> $impl_name { + $impl_name { + base: $crate::ComposeBase::new(), + // Each `&'static` vtable reference goes through a `const` item in its + // own block scope; this works around the fact that constant promotion + // does not promote arbitrary `const fn` calls to `'static`. + identity: { + const C: $crate::IInspectable_Vtbl = + <$crate::IInspectable_Vtbl>::new::<$impl_name, $first_iface, -1>(); + &C + }, + $($inits)* + this: self, + count: $crate::imp::WeakRefCount::new(), + } + } + + /// Converts a value into a [`StaticComObject`](::windows_core::StaticComObject) + /// suitable for storage in a static (global) variable. + pub const fn into_static(self) -> $crate::StaticComObject { + $crate::StaticComObject::from_outer(self.into_outer()) + } + } + + impl ::core::ops::Deref for $impl_name { + type Target = $name; + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.this + } + } + + impl $crate::IUnknownImpl for $impl_name { + type Impl = $name; + + #[inline(always)] + fn get_impl(&self) -> &Self::Impl { + &self.this + } + + #[inline(always)] + fn get_impl_mut(&mut self) -> &mut Self::Impl { + &mut self.this + } + + #[inline(always)] + fn into_inner(self) -> Self::Impl { + self.this + } + + #[inline(always)] + fn AddRef(&self) -> u32 { + self.count.add_ref() + } + + #[inline(always)] + unsafe fn Release(self_: *mut Self) -> u32 { + unsafe { + let remaining = (*self_).count.release(); + if remaining == 0 { + _ = $crate::imp::Box::from_raw(self_); + } + remaining + } + } + + #[inline(always)] + fn is_reference_count_one(&self) -> bool { + self.count.is_one() + } + + unsafe fn GetTrustLevel(&self, value: *mut i32) -> $crate::HRESULT { + if value.is_null() { + return $crate::imp::E_POINTER; + } + unsafe { *value = 0; } + $crate::HRESULT(0) + } + + fn to_object(&self) -> $crate::ComObject { + self.count.add_ref(); + unsafe { + $crate::ComObject::from_raw( + ::core::ptr::NonNull::new_unchecked(self as *const Self as *mut Self), + ) + } + } + + unsafe fn QueryInterface( + &self, + iid: *const $crate::GUID, + interface: *mut *mut ::core::ffi::c_void, + ) -> $crate::HRESULT { + unsafe { + if iid.is_null() || interface.is_null() { + return $crate::imp::E_POINTER; + } + let iid = *iid; + let interface_ptr: *const ::core::ffi::c_void = 'found: { + if iid == <$crate::IUnknown as $crate::Interface>::IID + || iid == <$crate::IInspectable as $crate::Interface>::IID + || iid == <$crate::imp::IAgileObject as $crate::Interface>::IID + { + break 'found &self.identity as *const _ as *const ::core::ffi::c_void; + } + $( + if <<$qi_iface as $crate::Interface>::Vtable>::matches(&iid) { + break 'found &self.$qi_iface as *const _ as *const ::core::ffi::c_void; + } + )* + #[cfg(windows)] + if iid == <$crate::imp::IMarshal as $crate::Interface>::IID { + return $crate::imp::marshaler( + ::to_interface::<$crate::IUnknown>(self), + interface, + ); + } + if iid == $crate::DYNAMIC_CAST_IID { + // Special protocol: write the `&dyn Any` directly to the + // out-parameter without reference-counting. + (interface as *mut *const dyn ::core::any::Any) + .write(self as &dyn ::core::any::Any as *const dyn ::core::any::Any); + return $crate::HRESULT(0); + } + let tear_off_ptr = self.count.query( + &iid, + &self.identity as *const _ as *mut _, + ); + if !tear_off_ptr.is_null() { + *interface = tear_off_ptr; + return $crate::HRESULT(0); + } + if let ::core::option::Option::Some(base) = self.base.as_option() { + return $crate::Interface::query( + base, + &iid as *const $crate::GUID, + interface, + ); + } + *interface = ::core::ptr::null_mut(); + return $crate::imp::E_NOINTERFACE; + }; + debug_assert!(!interface_ptr.is_null()); + *interface = interface_ptr as *mut ::core::ffi::c_void; + self.count.add_ref(); + $crate::HRESULT(0) + } + } + } + + impl $crate::ComObjectInner for $name { + type Outer = $impl_name; + + fn into_object(self) -> $crate::ComObject { + let boxed = $crate::imp::Box::<$impl_name>::new(self.into_outer()); + unsafe { + let ptr = $crate::imp::Box::into_raw(boxed); + $crate::ComObject::from_raw(::core::ptr::NonNull::new_unchecked(ptr)) + } + } + } + + impl $crate::Compose for $name { + unsafe fn compose<'a>( + implementation: Self, + ) -> ($crate::IInspectable, &'a mut ::core::option::Option<$crate::IInspectable>) { + unsafe { + let inspectable: $crate::IInspectable = implementation.into(); + let identity_ptr: *mut ::core::ffi::c_void = $crate::Interface::as_raw(&inspectable); + // `base` lives in the pointer-slot before `identity` (ComposeBase is + // repr(transparent) over Option). + let base_ptr = (identity_ptr as *mut *mut ::core::ffi::c_void).sub(1) + as *mut ::core::option::Option<$crate::IInspectable>; + (inspectable, &mut *base_ptr) + } + } + } + + impl ::core::convert::From<$name> for $crate::IUnknown { + #[inline(always)] + fn from(this: $name) -> Self { + let com_object = $crate::ComObject::new(this); + com_object.into_interface() + } + } + + impl ::core::convert::From<$name> for $crate::IInspectable { + #[inline(always)] + fn from(this: $name) -> Self { + let com_object = $crate::ComObject::new(this); + com_object.into_interface() + } + } + + impl $crate::ComObjectInterface<$crate::IUnknown> for $impl_name { + #[inline(always)] + fn as_interface_ref(&self) -> $crate::InterfaceRef<'_, $crate::IUnknown> { + unsafe { ::core::mem::transmute(&self.identity) } + } + } + + impl $crate::ComObjectInterface<$crate::IInspectable> for $impl_name { + #[inline(always)] + fn as_interface_ref(&self) -> $crate::InterfaceRef<'_, $crate::IInspectable> { + unsafe { ::core::mem::transmute(&self.identity) } + } + } + }; +} + +// --- Offset computation ----------------------------------------------------------------- +// +// `into_outer` writes each interface vtable as a `&'static IFoo_Vtbl` produced by +// `IFoo_Vtbl::new::()`. The offsets follow the proc-macro convention: +// identity is at -1, the first interface chain at -2, the second at -3, and so on. +// +// `__implement_decl_offset_negate!()` takes a unary-counted token list and emits the +// corresponding negative `isize` literal. Each `()` in the input represents one +// pointer-sized slot of offset. The accumulator starts with `[() ()]` (= -2) before any +// interface is consumed; each interface push adds one more `()`. + +#[doc(hidden)] +#[macro_export] +macro_rules! __implement_decl_offset_negate { + (()) => { + -1isize + }; + (() ()) => { + -2isize + }; + (() () ()) => { + -3isize + }; + (() () () ()) => { + -4isize + }; + (() () () () ()) => { + -5isize + }; + (() () () () () ()) => { + -6isize + }; + (() () () () () () ()) => { + -7isize + }; + (() () () () () () () ()) => { + -8isize + }; + (() () () () () () () () ()) => { + -9isize + }; + (() () () () () () () () () ()) => { + -10isize + }; + (() () () () () () () () () () ()) => { + -11isize + }; + (() () () () () () () () () () () ()) => { + -12isize + }; + (() () () () () () () () () () () () ()) => { + -13isize + }; + (() () () () () () () () () () () () () ()) => { + -14isize + }; + (() () () () () () () () () () () () () () ()) => { + -15isize + }; + (() () () () () () () () () () () () () () () ()) => { + -16isize + }; // Hand-written implementers rarely declare more than a handful of interfaces; the + // hard cap is more than the practical maximum. If you hit this, split your + // implementation across multiple objects or use the proc-macro. +} + +// --- Per-interface impls ---------------------------------------------------------------- +// +// Recursive emission of: +// - `From for IFace` +// - `ComObjectInterface for Foo_Impl` +// - `AsImpl for IFace` +// +// Tracks an interface index (unary-counted) so that the `AsImpl::as_impl_ptr` thunk +// adjusts the pointer correctly: identity sits at `-1`, the first chain at `-2`, etc., so +// the offset from a vtable pointer to the `Foo_Impl` start is `2 + index` pointer-slots. + +#[doc(hidden)] +#[macro_export] +macro_rules! __implement_decl_per_iface_impls { + ($name:ident, $impl_name:ident, $(($iface:ident),)+ ) => { + $crate::__implement_decl_per_iface_impls!( + @walk + name: $name, + impl_name: $impl_name, + index: [ ], + remaining: [ $( ($iface) )+ ] + ); + }; + (@walk + name: $name:ident, + impl_name: $impl_name:ident, + index: [ $($index:tt)* ], + remaining: [ ($iface:ident) $($rest:tt)* ] + ) => { + impl ::core::convert::From<$name> for $iface { + #[inline(always)] + fn from(this: $name) -> Self { + let com_object = $crate::ComObject::new(this); + com_object.into_interface() + } + } + + impl $crate::ComObjectInterface<$iface> for $impl_name { + #[inline(always)] + fn as_interface_ref(&self) -> $crate::InterfaceRef<'_, $iface> { + unsafe { ::core::mem::transmute(&self.$iface) } + } + } + + impl $crate::AsImpl<$name> for $iface { + #[inline(always)] + unsafe fn as_impl_ptr(&self) -> ::core::ptr::NonNull<$name> { + unsafe { + let this = $crate::Interface::as_raw(self); + // 2 + index pointer-slots back from the vtable pointer = start of + // Foo_Impl (identity at -1, this chain at -(2 + index)). + let this = (this as *mut *mut ::core::ffi::c_void) + .sub($crate::__implement_decl_index_plus_two!($($index)*)) + as *mut $impl_name; + ::core::ptr::NonNull::new_unchecked( + ::core::ptr::addr_of!((*this).this) as *const $name as *mut $name, + ) + } + } + } + + $crate::__implement_decl_per_iface_impls!( + @walk + name: $name, + impl_name: $impl_name, + index: [ $($index)* () ], + remaining: [ $($rest)* ] + ); + }; + (@walk + name: $name:ident, + impl_name: $impl_name:ident, + index: [ $($index:tt)* ], + remaining: [ ] + ) => {}; +} + +// `index` is a unary count starting at 0 (empty); emit `2 + index` as a usize literal. +#[doc(hidden)] +#[macro_export] +macro_rules! __implement_decl_index_plus_two { + () => { + 2usize + }; + (()) => { + 3usize + }; + (() ()) => { + 4usize + }; + (() () ()) => { + 5usize + }; + (() () () ()) => { + 6usize + }; + (() () () () ()) => { + 7usize + }; + (() () () () () ()) => { + 8usize + }; + (() () () () () () ()) => { + 9usize + }; + (() () () () () () () ()) => { + 10usize + }; + (() () () () () () () () ()) => { + 11usize + }; + (() () () () () () () () () ()) => { + 12usize + }; + (() () () () () () () () () () ()) => { + 13usize + }; + (() () () () () () () () () () () ()) => { + 14usize + }; + (() () () () () () () () () () () () ()) => { + 15usize + }; + (() () () () () () () () () () () () () ()) => { + 16usize + }; + (() () () () () () () () () () () () () () ()) => { + 17usize + }; +} + +// --- Zip helper: pair each interface type with a fresh internal field name --------------- +// +// The user-facing macro accepts a bare comma-separated list of interface types like +// `[ IAsyncOperation, IAsyncInfo, ]`. macro_rules! can't tokenize that with a `tt` +// repetition because of `<`/`>` ambiguity, but `$ty` matches each entry as a single +// fragment. The zip then pairs each `ty` with an internal ident drawn from a fixed pool +// so the rest of the pipeline (which uses `(ident : ty)` pairs) is unchanged. + +#[doc(hidden)] +#[macro_export] +macro_rules! __implement_decl_g_zip { + // Step: pop one name and one type. + (@zip + ctx: $ctx:tt, + names: [ $name_head:ident $($name_rest:ident)* ], + tys: [ $ty_head:ty $(, $ty_rest:ty)* $(,)? ], + acc: [ $($acc:tt)* ] + ) => { + $crate::__implement_decl_g_zip! { + @zip + ctx: $ctx, + names: [ $($name_rest)* ], + tys: [ $($ty_rest),* ], + acc: [ $($acc)* ($name_head : $ty_head) ] + } + }; + + // Done: dispatch to the existing pipeline. + (@zip + ctx: { + generics: [ $($gp:ident),+ ], + wc: { $($wc:tt)* }, + vis: $impl_vis:vis, + name: $name:ident, + impl_name: $impl_name:ident, + }, + names: [ $($unused:ident)* ], + tys: [ ], + acc: [ $( ($iface:ident : $ifty:ty) )+ ] + ) => { + $crate::__implement_decl_g_first_iface! { + @find + generics: [ $($gp),+ ], + wc: { $($wc)* }, + vis: $impl_vis, + name: $name, + impl_name: $impl_name, + remaining: [ $( ($iface : $ifty) )+ ] + } + + $crate::__implement_decl_g_per_iface_impls! { + generics: [ $($gp),+ ], + wc: { $($wc)* }, + name: $name, + impl_name: $impl_name, + interfaces: [ $( ($iface : $ifty) )+ ] + } + }; +} + +// --- Entry helper: capture the first interface, kick off the main walk ----------------- + +#[doc(hidden)] +#[macro_export] +macro_rules! __implement_decl_g_first_iface { + (@find + generics: [ $($gp:ident),+ ], + wc: { $($wc:tt)* }, + vis: $impl_vis:vis, + name: $name:ident, + impl_name: $impl_name:ident, + remaining: [ ($first_iface:ident : $first_ifty:ty) $($rest:tt)* ] + ) => { + $crate::__implement_decl_g_struct! { + @walk + generics: [ $($gp),+ ], + wc: { $($wc)* }, + vis: $impl_vis, + name: $name, + impl_name: $impl_name, + first_ifty: $first_ifty, + fields: { }, + consts: { }, + inits: { }, + qi_pairs: [ ], + offset: [ () () ], + remaining: [ ($first_iface : $first_ifty) $($rest)* ] + } + }; +} + +// --- Main accumulator ------------------------------------------------------------------- +// +// Walks the interface list and accumulates: +// * struct field declarations (`fields`), +// * associated-constant declarations on `Foo_Impl` (`consts`), +// * struct field initializers for `into_outer` (`inits`), +// * `(field_ident, ifty)` pairs for `QueryInterface` matching (`qi_pairs`), +// * a unary-counted offset (`offset`) so the per-chain vtable knows its slot. +// +// Hygiene: like `implement_decl!`, the accumulator carries *data* only. References to +// `self`, `iid`, and the `'found` label are emitted from the single base arm at the end. + +#[doc(hidden)] +#[macro_export] +macro_rules! __implement_decl_g_struct { + // One more interface to consume. + (@walk + generics: [ $($gp:ident),+ ], + wc: { $($wc:tt)* }, + vis: $impl_vis:vis, + name: $name:ident, + impl_name: $impl_name:ident, + first_ifty: $first_ifty:ty, + fields: { $($fields:tt)* }, + consts: { $($consts:tt)* }, + inits: { $($inits:tt)* }, + qi_pairs: [ $($qi_pairs:tt)* ], + offset: [ $($offset:tt)* ], + remaining: [ ($iface:ident : $ifty:ty) $($rest:tt)* ] + ) => { + $crate::__implement_decl_g_struct! { + @walk + generics: [ $($gp),+ ], + wc: { $($wc)* }, + vis: $impl_vis, + name: $name, + impl_name: $impl_name, + first_ifty: $first_ifty, + fields: { + $($fields)* + #[allow(non_snake_case)] + pub $iface: &'static <$ifty as $crate::Interface>::Vtable, + }, + consts: { + $($consts)* + // Per-chain vtable lives as an associated constant on the generic + // `impl Foo_Impl` block. Associated constants are allowed to + // reference the outer impl's generic parameters; an in-function + // `const C: ... = ...;` would not be (E0401). + #[allow(non_upper_case_globals)] + const $iface: <$ifty as $crate::Interface>::Vtable = + <<$ifty as $crate::Interface>::Vtable>::new::< + Self, + { $crate::__implement_decl_offset_negate!($($offset)*) }, + >(); + }, + inits: { + $($inits)* + $iface: &<$impl_name < $($gp),+ >>::$iface, + }, + qi_pairs: [ $($qi_pairs)* ($iface : $ifty) ], + offset: [ $($offset)* () ], + remaining: [ $($rest)* ] + } + }; + + // No more interfaces: emit struct + all impls. + (@walk + generics: [ $($gp:ident),+ ], + wc: { $($wc:tt)* }, + vis: $impl_vis:vis, + name: $name:ident, + impl_name: $impl_name:ident, + first_ifty: $first_ifty:ty, + fields: { $($fields:tt)* }, + consts: { $($consts:tt)* }, + inits: { $($inits:tt)* }, + qi_pairs: [ $(($qi_iface:ident : $qi_ifty:ty))* ], + offset: [ $($offset:tt)* ], + remaining: [ ] + ) => { + #[repr(C)] + #[allow(non_camel_case_types, non_snake_case)] + $impl_vis struct $impl_name < $($gp),+ > + where $($wc)* + { + pub base: $crate::ComposeBase, + pub identity: &'static $crate::IInspectable_Vtbl, + $($fields)* + pub this: $name < $($gp),+ >, + pub count: $crate::imp::WeakRefCount, + } + + impl< $($gp),+ > $impl_name < $($gp),+ > + where $($wc)* + { + // The identity vtable, like the per-interface vtables below, has to live + // on an associated constant rather than as an in-function `const C: T` + // because the implementer's generic parameters flow into the vtable's + // type parameters (via `Self`). + #[allow(non_upper_case_globals)] + const __VTABLE_IDENTITY: $crate::IInspectable_Vtbl = + <$crate::IInspectable_Vtbl>::new::(); + + $($consts)* + } + + impl< $($gp),+ > $name < $($gp),+ > + where $($wc)* + { + /// Constructs the outer (boxed) representation of this implementer. + #[doc(hidden)] + #[inline(always)] + #[allow(non_snake_case)] + // Not `const`: a generic `fn` cannot be `const fn` while reading associated + // constants whose values depend on `Self`'s type arguments. + pub fn into_outer(self) -> $impl_name < $($gp),+ > { + $impl_name { + base: $crate::ComposeBase::new(), + identity: &<$impl_name < $($gp),+ >>::__VTABLE_IDENTITY, + $($inits)* + this: self, + count: $crate::imp::WeakRefCount::new(), + } + } + } + + impl< $($gp),+ > ::core::ops::Deref for $impl_name < $($gp),+ > + where $($wc)* + { + type Target = $name < $($gp),+ >; + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.this + } + } + + impl< $($gp),+ > $crate::IUnknownImpl for $impl_name < $($gp),+ > + where $($wc)* + { + type Impl = $name < $($gp),+ >; + + #[inline(always)] + fn get_impl(&self) -> &Self::Impl { + &self.this + } + + #[inline(always)] + fn get_impl_mut(&mut self) -> &mut Self::Impl { + &mut self.this + } + + #[inline(always)] + fn into_inner(self) -> Self::Impl { + self.this + } + + #[inline(always)] + fn AddRef(&self) -> u32 { + self.count.add_ref() + } + + #[inline(always)] + unsafe fn Release(self_: *mut Self) -> u32 { + unsafe { + let remaining = (*self_).count.release(); + if remaining == 0 { + _ = $crate::imp::Box::from_raw(self_); + } + remaining + } + } + + #[inline(always)] + fn is_reference_count_one(&self) -> bool { + self.count.is_one() + } + + unsafe fn GetTrustLevel(&self, value: *mut i32) -> $crate::HRESULT { + if value.is_null() { + return $crate::imp::E_POINTER; + } + unsafe { *value = 0; } + $crate::HRESULT(0) + } + + fn to_object(&self) -> $crate::ComObject { + self.count.add_ref(); + unsafe { + $crate::ComObject::from_raw( + ::core::ptr::NonNull::new_unchecked(self as *const Self as *mut Self), + ) + } + } + + unsafe fn QueryInterface( + &self, + iid: *const $crate::GUID, + interface: *mut *mut ::core::ffi::c_void, + ) -> $crate::HRESULT { + unsafe { + if iid.is_null() || interface.is_null() { + return $crate::imp::E_POINTER; + } + let iid = *iid; + let interface_ptr: *const ::core::ffi::c_void = 'found: { + if iid == <$crate::IUnknown as $crate::Interface>::IID + || iid == <$crate::IInspectable as $crate::Interface>::IID + || iid == <$crate::imp::IAgileObject as $crate::Interface>::IID + { + break 'found &self.identity as *const _ as *const ::core::ffi::c_void; + } + $( + if <<$qi_ifty as $crate::Interface>::Vtable>::matches(&iid) { + break 'found &self.$qi_iface as *const _ as *const ::core::ffi::c_void; + } + )* + #[cfg(windows)] + if iid == <$crate::imp::IMarshal as $crate::Interface>::IID { + return $crate::imp::marshaler( + ::to_interface::<$crate::IUnknown>(self), + interface, + ); + } + if iid == $crate::DYNAMIC_CAST_IID { + (interface as *mut *const dyn ::core::any::Any) + .write(self as &dyn ::core::any::Any as *const dyn ::core::any::Any); + return $crate::HRESULT(0); + } + let tear_off_ptr = self.count.query( + &iid, + &self.identity as *const _ as *mut _, + ); + if !tear_off_ptr.is_null() { + *interface = tear_off_ptr; + return $crate::HRESULT(0); + } + if let ::core::option::Option::Some(base) = self.base.as_option() { + return $crate::Interface::query( + base, + &iid as *const $crate::GUID, + interface, + ); + } + *interface = ::core::ptr::null_mut(); + return $crate::imp::E_NOINTERFACE; + }; + debug_assert!(!interface_ptr.is_null()); + *interface = interface_ptr as *mut ::core::ffi::c_void; + self.count.add_ref(); + $crate::HRESULT(0) + } + } + } + + impl< $($gp),+ > $crate::ComObjectInner for $name < $($gp),+ > + where $($wc)* + { + type Outer = $impl_name < $($gp),+ >; + + fn into_object(self) -> $crate::ComObject { + let boxed = $crate::imp::Box::<$impl_name < $($gp),+ >>::new(self.into_outer()); + unsafe { + let ptr = $crate::imp::Box::into_raw(boxed); + $crate::ComObject::from_raw(::core::ptr::NonNull::new_unchecked(ptr)) + } + } + } + + impl< $($gp),+ > $crate::Compose for $name < $($gp),+ > + where $($wc)* + { + unsafe fn compose<'a>( + implementation: Self, + ) -> ($crate::IInspectable, &'a mut ::core::option::Option<$crate::IInspectable>) { + unsafe { + let inspectable: $crate::IInspectable = implementation.into(); + let identity_ptr: *mut ::core::ffi::c_void = $crate::Interface::as_raw(&inspectable); + let base_ptr = (identity_ptr as *mut *mut ::core::ffi::c_void).sub(1) + as *mut ::core::option::Option<$crate::IInspectable>; + (inspectable, &mut *base_ptr) + } + } + } + + impl< $($gp),+ > ::core::convert::From<$name < $($gp),+ >> for $crate::IUnknown + where $($wc)* + { + #[inline(always)] + fn from(this: $name < $($gp),+ >) -> Self { + let com_object = $crate::ComObject::new(this); + com_object.into_interface() + } + } + + impl< $($gp),+ > ::core::convert::From<$name < $($gp),+ >> for $crate::IInspectable + where $($wc)* + { + #[inline(always)] + fn from(this: $name < $($gp),+ >) -> Self { + let com_object = $crate::ComObject::new(this); + com_object.into_interface() + } + } + + impl< $($gp),+ > $crate::ComObjectInterface<$crate::IUnknown> for $impl_name < $($gp),+ > + where $($wc)* + { + #[inline(always)] + fn as_interface_ref(&self) -> $crate::InterfaceRef<'_, $crate::IUnknown> { + unsafe { ::core::mem::transmute(&self.identity) } + } + } + + impl< $($gp),+ > $crate::ComObjectInterface<$crate::IInspectable> for $impl_name < $($gp),+ > + where $($wc)* + { + #[inline(always)] + fn as_interface_ref(&self) -> $crate::InterfaceRef<'_, $crate::IInspectable> { + unsafe { ::core::mem::transmute(&self.identity) } + } + } + }; +} + +// --- Per-interface impls ---------------------------------------------------------------- +// +// Emits `From> for IFace`, `ComObjectInterface for Foo_Impl`, and +// `AsImpl> for IFace`, mirroring the per-interface emission in `implement_decl!`. + +#[doc(hidden)] +#[macro_export] +macro_rules! __implement_decl_g_per_iface_impls { + ( + generics: [ $($gp:ident),+ ], + wc: { $($wc:tt)* }, + name: $name:ident, + impl_name: $impl_name:ident, + interfaces: [ $( ($iface:ident : $ifty:ty) )+ ] + ) => { + $crate::__implement_decl_g_per_iface_impls! { + @walk + generics: [ $($gp),+ ], + wc: { $($wc)* }, + name: $name, + impl_name: $impl_name, + index: [ ], + remaining: [ $( ($iface : $ifty) )+ ] + } + }; + + (@walk + generics: [ $($gp:ident),+ ], + wc: { $($wc:tt)* }, + name: $name:ident, + impl_name: $impl_name:ident, + index: [ $($index:tt)* ], + remaining: [ ($iface:ident : $ifty:ty) $($rest:tt)* ] + ) => { + impl< $($gp),+ > ::core::convert::From<$name < $($gp),+ >> for $ifty + where $($wc)* + { + #[inline(always)] + fn from(this: $name < $($gp),+ >) -> Self { + let com_object = $crate::ComObject::new(this); + com_object.into_interface() + } + } + + impl< $($gp),+ > $crate::ComObjectInterface<$ifty> for $impl_name < $($gp),+ > + where $($wc)* + { + #[inline(always)] + fn as_interface_ref(&self) -> $crate::InterfaceRef<'_, $ifty> { + unsafe { ::core::mem::transmute(&self.$iface) } + } + } + + impl< $($gp),+ > $crate::AsImpl<$name < $($gp),+ >> for $ifty + where $($wc)* + { + #[inline(always)] + unsafe fn as_impl_ptr(&self) -> ::core::ptr::NonNull<$name < $($gp),+ >> { + unsafe { + let this = $crate::Interface::as_raw(self); + let this = (this as *mut *mut ::core::ffi::c_void) + .sub($crate::__implement_decl_index_plus_two!($($index)*)) + as *mut $impl_name < $($gp),+ >; + ::core::ptr::NonNull::new_unchecked( + ::core::ptr::addr_of!((*this).this) + as *const $name < $($gp),+ > + as *mut $name < $($gp),+ >, + ) + } + } + } + + $crate::__implement_decl_g_per_iface_impls! { + @walk + generics: [ $($gp),+ ], + wc: { $($wc)* }, + name: $name, + impl_name: $impl_name, + index: [ $($index)* () ], + remaining: [ $($rest)* ] + } + }; + + (@walk + generics: [ $($gp:ident),+ ], + wc: { $($wc:tt)* }, + name: $name:ident, + impl_name: $impl_name:ident, + index: [ $($index:tt)* ], + remaining: [ ] + ) => {}; +} diff --git a/third_party/windows-winui/windows-core/src/in_ref.rs b/third_party/windows-winui/windows-core/src/in_ref.rs new file mode 100644 index 0000000000..68bda53b36 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/in_ref.rs @@ -0,0 +1,89 @@ +use super::*; +use core::mem::transmute; + +/// A borrowed type with the same memory layout as the type itself that can be used to construct ABI-compatible function signatures. +#[repr(transparent)] +pub struct InRef<'a, T: Type>(T::Abi, core::marker::PhantomData<&'a T>); + +impl> InRef<'_, T> { + /// Returns `true` if the argument is null. + pub fn is_null(&self) -> bool { + T::is_null(&self.0) + } + + /// Converts the argument to a [`Result<&T>`] reference. + pub fn ok(&self) -> Result<&T> { + self.as_ref() + .ok_or_else(|| Error::from_hresult(imp::E_POINTER)) + } + + /// Converts the argument to a [`Option<&T>`] reference. + pub fn as_ref(&self) -> Option<&T> { + if self.is_null() { + None + } else { + unsafe { Some(self.assume_init_ref()) } + } + } + + /// Converts the argument to a `&T` reference. + /// + /// # Panics + /// + /// Panics if the argument is null. + #[track_caller] + pub fn unwrap(&self) -> &T { + self.as_ref() + .expect("called `InRef::unwrap` on a null value") + } + + /// Converts the argument to an [`Option`] by cloning the reference. + pub fn cloned(&self) -> Option { + self.as_ref().cloned() + } + + unsafe fn assume_init_ref(&self) -> &T { + unsafe { T::assume_init_ref(&self.0) } + } +} + +impl> Default for InRef<'_, T> { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} + +impl> core::ops::Deref for InRef<'_, T> { + type Target = T::Default; + fn deref(&self) -> &Self::Target { + unsafe { transmute(&self.0) } + } +} + +impl<'a, T: Type> From<&'a Option> for InRef<'a, T> +where + T: TypeKind, +{ + fn from(from: &'a Option) -> Self { + unsafe { core::mem::transmute_copy(from) } + } +} + +impl<'a, T: Type> From> for InRef<'a, T> +where + T: TypeKind, +{ + fn from(from: Option<&'a T>) -> Self { + if let Some(from) = from { + unsafe { core::mem::transmute_copy(from) } + } else { + unsafe { core::mem::zeroed() } + } + } +} + +impl<'a, T: Type> From<&'a T> for InRef<'a, T> { + fn from(from: &'a T) -> Self { + unsafe { core::mem::transmute_copy(from) } + } +} diff --git a/third_party/windows-winui/windows-core/src/inspectable.rs b/third_party/windows-winui/windows-core/src/inspectable.rs new file mode 100644 index 0000000000..e16a46b482 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/inspectable.rs @@ -0,0 +1,117 @@ +use super::*; +use core::ffi::c_void; +use core::ptr::null_mut; + +/// Parent interface for all WinRT interfaces. +/// +/// A WinRT object that may be used as a polymorphic stand-in for any WinRT class, interface, or boxed value. +/// [`IInspectable`] represents the +/// [IInspectable](https://docs.microsoft.com/en-us/windows/win32/api/inspectable/nn-inspectable-iinspectable) +/// interface. +#[repr(transparent)] +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct IInspectable(pub IUnknown); + +interface_hierarchy!(IInspectable, IUnknown); + +impl IInspectable { + /// Returns the canonical type name for the underlying object. + pub fn GetRuntimeClassName(&self) -> Result { + unsafe { + let mut abi = null_mut(); + (self.vtable().GetRuntimeClassName)(self.as_raw(), &mut abi).ok()?; + Ok(core::mem::transmute::<*mut c_void, HSTRING>(abi)) + } + } + + /// Gets the trust level of the current object. + pub fn GetTrustLevel(&self) -> Result { + unsafe { + let mut value = 0; + (self.vtable().GetTrustLevel)(self.as_raw(), &mut value).ok()?; + Ok(value) + } + } +} + +#[doc(hidden)] +#[repr(C)] +pub struct IInspectable_Vtbl { + pub base: IUnknown_Vtbl, + pub GetIids: unsafe extern "system" fn( + this: *mut c_void, + count: *mut u32, + values: *mut *mut GUID, + ) -> HRESULT, + pub GetRuntimeClassName: + unsafe extern "system" fn(this: *mut c_void, value: *mut *mut c_void) -> HRESULT, + pub GetTrustLevel: unsafe extern "system" fn(this: *mut c_void, value: *mut i32) -> HRESULT, +} + +unsafe impl Interface for IInspectable { + type Vtable = IInspectable_Vtbl; + const IID: GUID = GUID::from_u128(0xaf86e2e0_b12d_4c6a_9c5a_d7aa65101e90); +} + +impl RuntimeType for IInspectable { + const SIGNATURE: imp::ConstBuffer = imp::ConstBuffer::from_slice(b"cinterface(IInspectable)"); + const NAME: imp::ConstBuffer = imp::ConstBuffer::from_slice(b"Object"); +} + +impl RuntimeName for IInspectable {} + +impl IInspectable_Vtbl { + pub const fn new() -> Self { + unsafe extern "system" fn GetIids( + _: *mut c_void, + count: *mut u32, + values: *mut *mut GUID, + ) -> HRESULT { + unsafe { + if count.is_null() || values.is_null() { + return imp::E_POINTER; + } + // Note: even if we end up implementing this in future, it still doesn't need a this pointer + // since the data to be returned is type- not instance-specific so can be shared for all + // interfaces. + *count = 0; + *values = null_mut(); + HRESULT(0) + } + } + unsafe extern "system" fn GetRuntimeClassName( + _: *mut c_void, + value: *mut *mut c_void, + ) -> HRESULT { + unsafe { + if value.is_null() { + return imp::E_POINTER; + } + + let name = T::RUNTIME_CLASS_NAME; + let name_str = core::str::from_utf8_unchecked(name.as_slice()); + *value = core::mem::transmute::(name_str.into()); + + HRESULT(0) + } + } + unsafe extern "system" fn GetTrustLevel( + this: *mut c_void, + value: *mut i32, + ) -> HRESULT { + unsafe { + if value.is_null() { + return imp::E_POINTER; + } + let this = (this as *mut *mut c_void).offset(OFFSET) as *mut T; + (*this).GetTrustLevel(value) + } + } + Self { + base: IUnknown_Vtbl::new::(), + GetIids, + GetRuntimeClassName: GetRuntimeClassName::, + GetTrustLevel: GetTrustLevel::, + } + } +} diff --git a/third_party/windows-winui/windows-core/src/interface.rs b/third_party/windows-winui/windows-core/src/interface.rs new file mode 100644 index 0000000000..324ceb1b10 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/interface.rs @@ -0,0 +1,329 @@ +use super::*; +use core::any::Any; +use core::ffi::c_void; +use core::marker::PhantomData; +use core::mem::{forget, transmute_copy, MaybeUninit}; +use core::ptr::NonNull; + +/// Provides low-level access to an interface vtable. +/// +/// This trait is automatically implemented by the generated bindings and should not be +/// implemented manually. +/// +/// # Safety +pub unsafe trait Interface: Sized + Clone { + #[doc(hidden)] + type Vtable; + + /// The `GUID` associated with the interface. + const IID: GUID; + + #[doc(hidden)] + const UNKNOWN: bool = true; + + /// A reference to the interface's vtable + #[doc(hidden)] + #[inline(always)] + fn vtable(&self) -> &Self::Vtable { + // SAFETY: the implementor of the trait guarantees that `Self` is castable to its vtable + unsafe { self.assume_vtable::() } + } + + /// Cast this interface as a reference to the supplied interfaces `Vtable` + /// + /// # Safety + /// + /// This is safe if `T` is an equivalent interface to `Self` or a super interface. + /// In other words, `T::Vtable` must be equivalent to the beginning of `Self::Vtable`. + #[doc(hidden)] + #[inline(always)] + unsafe fn assume_vtable(&self) -> &T::Vtable { + unsafe { &**(self.as_raw() as *mut *mut T::Vtable) } + } + + /// Returns the raw COM interface pointer. The resulting pointer continues to be owned by the `Interface` implementation. + #[inline(always)] + fn as_raw(&self) -> *mut c_void { + // SAFETY: implementors of this trait must guarantee that the implementing type has a pointer in-memory representation + unsafe { transmute_copy(self) } + } + + /// Returns the raw COM interface pointer and releases ownership. It the caller's responsibility to release the COM interface pointer. + #[inline(always)] + fn into_raw(self) -> *mut c_void { + // SAFETY: implementors of this trait must guarantee that the implementing type has a pointer in-memory representation + let raw = self.as_raw(); + forget(self); + raw + } + + /// Creates an `Interface` by taking ownership of the `raw` COM interface pointer. + /// + /// # Safety + /// + /// The `raw` pointer must be owned by the caller and represent a valid COM interface pointer. In other words, + /// it must point to a vtable beginning with the `IUnknown` function pointers and match the vtable of `Interface`. + unsafe fn from_raw(raw: *mut c_void) -> Self { + unsafe { transmute_copy(&raw) } + } + + /// Creates an `Interface` that is valid so long as the `raw` COM interface pointer is valid. + /// + /// # Safety + /// + /// The `raw` pointer must be a valid COM interface pointer. In other words, it must point to a vtable + /// beginning with the `IUnknown` function pointers and match the vtable of `Interface`. + #[inline(always)] + unsafe fn from_raw_borrowed(raw: &*mut c_void) -> Option<&Self> { + unsafe { + if raw.is_null() { + None + } else { + Some(transmute_copy(&raw)) + } + } + } + + /// Attempts to cast the current interface to another interface using `QueryInterface`. + /// + /// The name `cast` is preferred over `query` because there is a WinRT method named `query` + /// but not one named `cast`. + #[inline(always)] + fn cast(&self) -> Result { + // SAFETY: `result` is valid for writing an interface pointer, and casting the result + // pointer as `T` on success is safe because we use the `IID` tied to `T`, which the + // implementor of `Interface` has guaranteed is correct. + unsafe { + // If `query()` fails, propagate the failure to the caller and ignore the contents + // of `result` (which will _not_ be dropped, because `MaybeUninit` intentionally + // does not drop its contents). This guards against COM implementations that store + // non-null values in `result` but still return `E_NOINTERFACE`. + let mut result = MaybeUninit::>::zeroed(); + self.query(&T::IID, result.as_mut_ptr() as _).ok()?; + + // `query()` succeeded; still double-check that the output pointer is non-null. + if let Some(obj) = result.assume_init() { + Ok(obj) + } else { + Err(imp::E_POINTER.into()) + } + } + } + + /// This casts the given COM interface to [`&dyn Any`]. + /// + /// Applications should generally _not_ call this method directly. Use + /// [`Interface::cast_object_ref`] or [`Interface::cast_object`] instead. + /// + /// `T` must be a type annotated with `#[implement]`; this is enforced at compile time by + /// the generic constraints. The returned `&dyn Any` refers to the _outer_ implementation + /// object generated by `#[implement]` (e.g. `MyApp_Impl`), not the inner `MyApp` type. + /// + /// Returns `Err(E_NOINTERFACE)` if the object is not a Rust object, not `T`, or contains + /// non-static lifetimes. + /// + /// # Safety + /// + /// **IMPORTANT!** This uses a non-standard `QueryInterface` protocol identified by + /// `DYNAMIC_CAST_IID` (there is no `IDynamicCast` interface). Objects that recognize + /// `DYNAMIC_CAST_IID` store their `&dyn Any` directly at the interface pointer passed to + /// `QueryInterface`. The returned value is twice the size of a normal pointer + /// (`size_of::<&dyn Any>() == 2 * size_of::<*const c_void>()`), so callers cannot pass + /// `&mut ptr` for an ordinary single-pointer-sized pointer. Only this method understands + /// the protocol. + /// + /// The `QueryInterface` implementation _does not_ `AddRef` the object. The caller must + /// guarantee the liveness of the COM object — typically by tying the lifetime of the + /// `IUnknown*` used for the call to the lifetime of the returned `&dyn Any`. + /// + /// This method preserves type safety and relies on these invariants: + /// + /// * All `QueryInterface` implementations that recognize `DYNAMIC_CAST_IID` are generated by + /// the `#[implement]` macro and respect the rules described here. + #[inline(always)] + fn cast_to_any(&self) -> Result<&dyn Any> + where + T: ComObjectInner, + T::Outer: Any + 'static + IUnknownImpl, + { + unsafe { + let mut any_ref_arg: MaybeUninit<&dyn Any> = MaybeUninit::zeroed(); + self.query( + &DYNAMIC_CAST_IID, + any_ref_arg.as_mut_ptr() as *mut *mut c_void, + ) + .ok()?; + Ok(any_ref_arg.assume_init()) + } + } + + /// Returns `true` if the given COM interface refers to an implementation of `T`. + /// + /// `T` must be a type annotated with `#[implement]`; this is enforced at compile time by + /// the generic constraints. + /// + /// Returns `false` if the object is not a Rust object, not `T`, or contains non-static + /// lifetimes. + #[inline(always)] + fn is_object(&self) -> bool + where + T: ComObjectInner, + T::Outer: Any + 'static + IUnknownImpl, + { + if let Ok(any) = self.cast_to_any::() { + any.is::() + } else { + false + } + } + + /// Casts the given COM interface to `&dyn Any`, returning a reference to the "outer" + /// object (e.g. `&MyApp_Impl`), not the inner `&MyApp`. + /// + /// `T` must be a type annotated with `#[implement]`; this is enforced at compile time by + /// the generic constraints. + /// + /// Returns `Err(E_NOINTERFACE)` if the object is not a Rust object, not `T`, or contains + /// non-static lifetimes. + /// + /// The returned value is borrowed; use [`Interface::cast_object`] for an owned (counted) + /// reference. + #[inline(always)] + fn cast_object_ref(&self) -> Result<&T::Outer> + where + T: ComObjectInner, + T::Outer: Any + 'static + IUnknownImpl, + { + let any: &dyn Any = self.cast_to_any::()?; + if let Some(outer) = any.downcast_ref::() { + Ok(outer) + } else { + Err(imp::E_NOINTERFACE.into()) + } + } + + /// Casts the given COM interface to `&dyn Any`, returning a reference to the "outer" + /// object (e.g. `MyApp_Impl`), not the inner `MyApp`. + /// + /// `T` must be a type annotated with `#[implement]`; this is enforced at compile time by + /// the generic constraints. + /// + /// Returns `Err(E_NOINTERFACE)` if the object is not a Rust object, not `T`, or contains + /// non-static lifetimes. + /// + /// The returned value is an owned (counted) reference: this function calls `AddRef`. Use + /// [`Interface::cast_object_ref`] to avoid `AddRef` / `Release` overhead if you do not need + /// ownership. + #[inline(always)] + fn cast_object(&self) -> Result> + where + T: ComObjectInner, + T::Outer: Any + 'static + IUnknownImpl, + { + let object_ref = self.cast_object_ref::()?; + Ok(object_ref.to_object()) + } + + /// Attempts to create a [`Weak`] reference to this object. + fn downgrade(&self) -> Result> { + self.cast::() + .map(|source| Weak::downgrade(&source)) + } + + /// Call `QueryInterface` on this interface + /// + /// # Safety + /// + /// `interface` must be a non-null, valid pointer for writing an interface pointer. + #[inline(always)] + unsafe fn query(&self, iid: *const GUID, interface: *mut *mut c_void) -> HRESULT { + unsafe { + if Self::UNKNOWN { + (self.assume_vtable::().QueryInterface)(self.as_raw(), iid, interface) + } else { + panic!("Non-COM interfaces cannot be queried.") + } + } + } + + /// Creates an `InterfaceRef` for this reference. The `InterfaceRef` tracks lifetimes statically, + /// and eliminates the need for dynamic reference count adjustments (AddRef/Release). + fn to_ref(&self) -> InterfaceRef<'_, Self> { + InterfaceRef::from_interface(self) + } +} + +/// This has the same memory representation as `IFoo`, but represents a borrowed interface pointer. +/// +/// This type has no `Drop` impl; it does not AddRef/Release the given interface. However, because +/// it has a lifetime parameter, it always represents a non-null pointer to an interface. +#[repr(transparent)] +pub struct InterfaceRef<'a, I>(NonNull, PhantomData<&'a I>); + +impl Copy for InterfaceRef<'_, I> {} + +impl Clone for InterfaceRef<'_, I> { + fn clone(&self) -> Self { + *self + } +} + +impl core::fmt::Debug for InterfaceRef<'_, I> { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + ::fmt(&**self, f) + } +} + +impl InterfaceRef<'_, I> { + /// Creates an `InterfaceRef` from a raw pointer. _This is extremely dangerous, since there + /// is no lifetime tracking at all!_ + /// + /// # Safety + /// The caller must guarantee that the `'a` lifetime parameter is bound by context to a correct + /// lifetime. + #[inline(always)] + pub unsafe fn from_raw(ptr: NonNull) -> Self { + Self(ptr, PhantomData) + } + + /// Creates an `InterfaceRef` from an interface reference. This safely associates the lifetime + /// of the interface reference with the `'a` parameter of `InterfaceRef`. This allows for + /// lifetime checking _without_ calling AddRef/Release on the underlying lifetime, which can + /// improve efficiency. + #[inline(always)] + pub fn from_interface(interface: &I) -> Self { + unsafe { + // SAFETY: new_unchecked() should be valid because Interface::as_raw should always + // return a non-null pointer. + Self(NonNull::new_unchecked(interface.as_raw()), PhantomData) + } + } + + /// Calls AddRef on the underlying COM interface and returns an "owned" (counted) reference. + #[inline(always)] + pub fn to_owned(self) -> I { + (*self).clone() + } +} + +impl<'a, 'i: 'a, I: Interface> From<&'i I> for InterfaceRef<'a, I> { + #[inline(always)] + fn from(interface: &'a I) -> Self { + InterfaceRef::from_interface(interface) + } +} + +impl core::ops::Deref for InterfaceRef<'_, I> { + type Target = I; + + #[inline(always)] + fn deref(&self) -> &I { + unsafe { core::mem::transmute(self) } + } +} + +/// This IID identifies a special protocol, used by [`Interface::cast_to_any`]. This is _not_ +/// an ordinary COM interface; it uses special lifetime rules and a larger interface pointer. +/// See the comments on [`Interface::cast_to_any`]. +#[doc(hidden)] +pub const DYNAMIC_CAST_IID: GUID = GUID::from_u128(0xae49d5cb_143f_431c_874c_2729336e4eca); diff --git a/third_party/windows-winui/windows-core/src/interface_macro.rs b/third_party/windows-winui/windows-core/src/interface_macro.rs new file mode 100644 index 0000000000..20cbc3dd3c --- /dev/null +++ b/third_party/windows-winui/windows-core/src/interface_macro.rs @@ -0,0 +1,405 @@ +//! A `macro_rules!` declarative alternative to the `#[interface]` proc-macro. +//! +//! The proc-macro `#[interface]` in the `windows-interface` crate is the canonical way to +//! define a COM interface in this codebase, but it transitively pulls in `syn`, `quote`, +//! and `proc-macro2`. Consumers who disable the default `proc-macros` feature on +//! `windows-core` still need a way to declare COM interfaces; that is the role of the +//! [`interface_decl!`] macro defined here. +//! +//! ## Scope +//! +//! `interface_decl!` targets the dominant case: a COM interface whose direct parent is +//! `IUnknown`, whose methods either return `windows_core::Result<()>`, return nothing, +//! or return a raw ABI type (most commonly `HRESULT`) that is passed through unchanged +//! to the caller. +//! It does **not** support `Result` for non-unit `T` (the safe caller wrapper would +//! discard the value); model that case with a `*mut T` out-parameter and `Result<()>`. +//! It does **not** support `Ref` / `OutRef` parameters with the implicit `Param` / +//! `OutParam` bound generation that `#[interface]` provides — pass the underlying ABI +//! types instead. It does not support scoped (non-`IUnknown`) interfaces or +//! `IInspectable`-derived (WinRT) interfaces. The matching chain for derived custom +//! interfaces is not traversed; use the proc-macro for those cases. +//! +//! ## Syntax +//! +//! ```rust,ignore +//! use windows_core::*; +//! +//! interface_decl! { +//! unsafe trait IFoo(IFoo_Vtbl, IFoo_Impl) : IUnknown +//! = 0x094d70d6_5202_44b8_abb8_43860da5aca2 +//! { +//! unsafe fn Void(&self); +//! unsafe fn TryGetValue(&self, value: *mut i32) -> Result<()>; +//! } +//! } +//! ``` +//! +//! The struct ident (`IFoo`), the vtable struct ident (`IFoo_Vtbl`), and the implementation +//! trait ident (`IFoo_Impl`) are all spelled out by the caller; `macro_rules!` cannot +//! synthesize identifiers from another ident the way a proc-macro can. The IID is supplied +//! as a `u128` integer literal. The generated interface struct is always `pub`, matching +//! the proc-macro's behavior. +//! +//! ## Semantics +//! +//! - `-> Result<()>` methods produce a safe caller-side wrapper that appends `.ok()` and a +//! vtable entry typed `-> HRESULT`. The thunk converts the implementer's `Result<()>` +//! back into an `HRESULT` via `Into`. +//! - A method with no return type produces a void-returning thunk. +//! - A method with any other return type (e.g. `-> HRESULT`) is passed through verbatim: +//! the vtable entry, the safe caller-side wrapper, and the implementer trait all use the +//! declared return type as-is, and the thunk forwards the implementer's value unchanged. + +/// Declares a COM interface inheriting from `IUnknown`, without using the `#[interface]` +/// proc-macro. See the module-level documentation for the supported syntax and scope. +#[macro_export] +macro_rules! interface_decl { + ( + unsafe trait $name:ident ( $vtbl:ident, $impl_trait:ident ) : $parent:ty = $iid:literal { + $($methods:tt)* + } + ) => { + // Struct + Interface + Debug. + $crate::imp::define_interface!($name, $vtbl, $iid); + // CanInto + From conversions to ancestors. + $crate::imp::interface_hierarchy!($name, $parent); + + impl ::core::ops::Deref for $name { + type Target = $parent; + #[inline] + fn deref(&self) -> &Self::Target { + // SAFETY: every interface declared via `define_interface!` is + // `#[repr(transparent)]` over `IUnknown`, and so is any custom parent + // declared the same way. The transmute is therefore a no-op layout-wise. + unsafe { ::core::mem::transmute(self) } + } + } + + impl $crate::RuntimeName for $name {} + + // Safe caller-side wrappers (inside `impl $name { ... }`, item-position — helper + // macros are permitted here and may emit a sequence of `fn` items). + impl $name { + $crate::__interface_decl_safe_wrappers!($($methods)*); + } + + // Implementation trait (inside `trait { ... }`, item-position). + #[allow(non_camel_case_types)] + pub trait $impl_trait: Sized + $crate::IUnknownImpl { + $crate::__interface_decl_trait_methods!($($methods)*); + } + + // Vtable struct + its `impl` block. These cannot use helper macros inside their + // field lists or struct-expression initializers, so we hand everything off to a + // TT-muncher accumulator that emits both items together when the method list is + // exhausted. + $crate::__interface_decl_vtbl! { + @start + name: $name, + vtbl: $vtbl, + impl_trait: $impl_trait, + parent: $parent, + methods: { $($methods)* } + } + }; +} + +// --- safe caller-side wrappers --- +// +// One arm per supported return-type shape (Result<()>, void); each arm peels off the +// head method and recurses on the tail. + +#[doc(hidden)] +#[macro_export] +macro_rules! __interface_decl_safe_wrappers { + () => {}; + ( + unsafe fn $mname:ident (&self $(, $aname:ident : $aty:ty)* $(,)? ) -> Result < () > ; + $($rest:tt)* + ) => { + #[inline] + pub unsafe fn $mname(&self $(, $aname: $aty)*) -> $crate::Result<()> { + unsafe { + ($crate::Interface::vtable(self).$mname)($crate::Interface::as_raw(self) $(, $aname)*).ok() + } + } + $crate::__interface_decl_safe_wrappers!($($rest)*); + }; + ( + unsafe fn $mname:ident (&self $(, $aname:ident : $aty:ty)* $(,)? ) ; + $($rest:tt)* + ) => { + #[inline] + pub unsafe fn $mname(&self $(, $aname: $aty)*) { + unsafe { + ($crate::Interface::vtable(self).$mname)($crate::Interface::as_raw(self) $(, $aname)*) + } + } + $crate::__interface_decl_safe_wrappers!($($rest)*); + }; + // Method with an arbitrary (non-`Result<()>`) return type — passed through verbatim. + // This arm must follow the `Result<()>` arm above because `$rty:ty` would also match + // `Result<()>`; macro_rules tries arms top-down. + ( + unsafe fn $mname:ident (&self $(, $aname:ident : $aty:ty)* $(,)? ) -> $rty:ty ; + $($rest:tt)* + ) => { + #[inline] + pub unsafe fn $mname(&self $(, $aname: $aty)*) -> $rty { + unsafe { + ($crate::Interface::vtable(self).$mname)($crate::Interface::as_raw(self) $(, $aname)*) + } + } + $crate::__interface_decl_safe_wrappers!($($rest)*); + }; +} + +// --- _Impl trait method declarations --- + +#[doc(hidden)] +#[macro_export] +macro_rules! __interface_decl_trait_methods { + () => {}; + ( + unsafe fn $mname:ident (&self $(, $aname:ident : $aty:ty)* $(,)? ) -> Result < () > ; + $($rest:tt)* + ) => { + unsafe fn $mname(&self $(, $aname: $aty)*) -> $crate::Result<()>; + $crate::__interface_decl_trait_methods!($($rest)*); + }; + ( + unsafe fn $mname:ident (&self $(, $aname:ident : $aty:ty)* $(,)? ) ; + $($rest:tt)* + ) => { + unsafe fn $mname(&self $(, $aname: $aty)*); + $crate::__interface_decl_trait_methods!($($rest)*); + }; + // Arbitrary non-`Result<()>` return type. Must follow the `Result<()>` arm. + ( + unsafe fn $mname:ident (&self $(, $aname:ident : $aty:ty)* $(,)? ) -> $rty:ty ; + $($rest:tt)* + ) => { + unsafe fn $mname(&self $(, $aname: $aty)*) -> $rty; + $crate::__interface_decl_trait_methods!($($rest)*); + }; +} + +// --- vtable struct + impl emission via TT-muncher accumulator --- +// +// We can't use helper macros in struct field lists or struct-expression initializers, so +// this macro accumulates three token lists (vtbl fields, vtbl initializers, thunk fn +// defs) and emits the whole `struct $vtbl { ... } impl $vtbl { ... }` block at the end. + +#[doc(hidden)] +#[macro_export] +macro_rules! __interface_decl_vtbl { + // Entry point: initialize empty accumulators. + (@start + name: $name:ident, + vtbl: $vtbl:ident, + impl_trait: $impl_trait:ident, + parent: $parent:ty, + methods: { $($methods:tt)* } + ) => { + $crate::__interface_decl_vtbl! { + @walk + name: $name, + vtbl: $vtbl, + impl_trait: $impl_trait, + parent: $parent, + fields: { }, + inits: { }, + thunks: { }, + rest: { $($methods)* } + } + }; + + // Result-returning method (only `Result<()>` is supported). + (@walk + name: $name:ident, + vtbl: $vtbl:ident, + impl_trait: $impl_trait:ident, + parent: $parent:ty, + fields: { $($fields:tt)* }, + inits: { $($inits:tt)* }, + thunks: { $($thunks:tt)* }, + rest: { + unsafe fn $mname:ident (&self $(, $aname:ident : $aty:ty)* $(,)? ) -> Result < () > ; + $($more:tt)* + } + ) => { + $crate::__interface_decl_vtbl! { + @walk + name: $name, + vtbl: $vtbl, + impl_trait: $impl_trait, + parent: $parent, + fields: { + $($fields)* + pub $mname: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void + $(, $aname: $aty)* + ) -> $crate::HRESULT, + }, + inits: { + $($inits)* + $mname: $mname::, + }, + thunks: { + $($thunks)* + unsafe extern "system" fn $mname( + this: *mut ::core::ffi::c_void + $(, $aname: $aty)* + ) -> $crate::HRESULT + where + Identity: $impl_trait, + { + let this_outer: &Identity = unsafe { + &*((this as *const *const ()).offset(OFFSET) as *const Identity) + }; + unsafe { ::$mname(this_outer $(, $aname)*) }.into() + } + }, + rest: { $($more)* } + } + }; + + // Void-returning method. + (@walk + name: $name:ident, + vtbl: $vtbl:ident, + impl_trait: $impl_trait:ident, + parent: $parent:ty, + fields: { $($fields:tt)* }, + inits: { $($inits:tt)* }, + thunks: { $($thunks:tt)* }, + rest: { + unsafe fn $mname:ident (&self $(, $aname:ident : $aty:ty)* $(,)? ) ; + $($more:tt)* + } + ) => { + $crate::__interface_decl_vtbl! { + @walk + name: $name, + vtbl: $vtbl, + impl_trait: $impl_trait, + parent: $parent, + fields: { + $($fields)* + pub $mname: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void + $(, $aname: $aty)* + ), + }, + inits: { + $($inits)* + $mname: $mname::, + }, + thunks: { + $($thunks)* + unsafe extern "system" fn $mname( + this: *mut ::core::ffi::c_void + $(, $aname: $aty)* + ) + where + Identity: $impl_trait, + { + let this_outer: &Identity = unsafe { + &*((this as *const *const ()).offset(OFFSET) as *const Identity) + }; + unsafe { ::$mname(this_outer $(, $aname)*) } + } + }, + rest: { $($more)* } + } + }; + + // Method with an arbitrary (non-`Result<()>`) return type — passed through verbatim. + // Must come after the `Result<()>` arm because `$rty:ty` would also match `Result<()>`. + (@walk + name: $name:ident, + vtbl: $vtbl:ident, + impl_trait: $impl_trait:ident, + parent: $parent:ty, + fields: { $($fields:tt)* }, + inits: { $($inits:tt)* }, + thunks: { $($thunks:tt)* }, + rest: { + unsafe fn $mname:ident (&self $(, $aname:ident : $aty:ty)* $(,)? ) -> $rty:ty ; + $($more:tt)* + } + ) => { + $crate::__interface_decl_vtbl! { + @walk + name: $name, + vtbl: $vtbl, + impl_trait: $impl_trait, + parent: $parent, + fields: { + $($fields)* + pub $mname: unsafe extern "system" fn( + this: *mut ::core::ffi::c_void + $(, $aname: $aty)* + ) -> $rty, + }, + inits: { + $($inits)* + $mname: $mname::, + }, + thunks: { + $($thunks)* + unsafe extern "system" fn $mname( + this: *mut ::core::ffi::c_void + $(, $aname: $aty)* + ) -> $rty + where + Identity: $impl_trait, + { + let this_outer: &Identity = unsafe { + &*((this as *const *const ()).offset(OFFSET) as *const Identity) + }; + unsafe { ::$mname(this_outer $(, $aname)*) } + } + }, + rest: { $($more)* } + } + }; + + // Base case: rest is empty, emit the struct and its impl block. + (@walk + name: $name:ident, + vtbl: $vtbl:ident, + impl_trait: $impl_trait:ident, + parent: $parent:ty, + fields: { $($fields:tt)* }, + inits: { $($inits:tt)* }, + thunks: { $($thunks:tt)* }, + rest: { } + ) => { + #[repr(C)] + #[doc(hidden)] + pub struct $vtbl { + pub base__: <$parent as $crate::Interface>::Vtable, + $($fields)* + } + + impl $vtbl { + pub const fn new() -> Self + where + Identity: $impl_trait, + { + $($thunks)* + Self { + base__: <<$parent as $crate::Interface>::Vtable>::new::(), + $($inits)* + } + } + + #[inline] + pub fn matches(iid: &$crate::GUID) -> bool { + *iid == <$name as $crate::Interface>::IID + } + } + }; +} diff --git a/third_party/windows-winui/windows-core/src/lib.rs b/third_party/windows-winui/windows-core/src/lib.rs new file mode 100644 index 0000000000..7d7e3fd98d --- /dev/null +++ b/third_party/windows-winui/windows-core/src/lib.rs @@ -0,0 +1,126 @@ +#![doc = include_str!("../readme.md")] +#![doc(html_no_source)] +#![debugger_visualizer(natvis_file = "../windows-core.natvis")] +#![cfg_attr(all(not(feature = "std")), no_std)] +#![expect( + non_snake_case, + non_camel_case_types, + dead_code, + clippy::missing_transmute_annotations, + clippy::upper_case_acronyms +)] + +#[cfg(windows)] +include!("windows.rs"); + +extern crate self as windows_core; + +extern crate alloc; + +use alloc::boxed::Box; + +#[doc(hidden)] +pub mod imp; + +mod agile_reference; +mod as_impl; +mod com_object; +mod compose; +#[cfg(feature = "std")] +mod event; +mod event_revoker; +mod guid; +mod implement_macro; +mod in_ref; +mod inspectable; +mod interface; +mod interface_macro; +mod out_param; +mod out_ref; +mod param; +mod param_value; +mod runtime_name; +mod runtime_type; +mod scoped_interface; +mod r#type; +mod unknown; +mod weak; + +pub use agile_reference::*; +pub use as_impl::*; +pub use com_object::*; +pub use compose::*; +#[cfg(feature = "std")] +pub use event::*; +pub use event_revoker::*; +pub use guid::*; +pub use in_ref::*; +pub use inspectable::*; +pub use interface::*; +pub use out_param::*; +pub use out_ref::*; +pub use param::*; +pub use param_value::*; +pub use r#type::*; +pub use runtime_name::*; +pub use runtime_type::*; +pub use scoped_interface::*; +pub use unknown::*; +pub use weak::*; +#[cfg(feature = "proc-macros")] +pub use windows_implement::implement; +#[cfg(feature = "proc-macros")] +pub use windows_interface::interface; +pub use windows_link::link; +pub use windows_result::*; +pub use windows_strings::*; + +/// Attempts to load the factory object for the given WinRT class. +/// This can be used to access COM interfaces implemented on a Windows Runtime class factory. +#[cfg(windows)] +pub fn factory() -> Result { + imp::load_factory::() +} + +impl Param for &HSTRING { + unsafe fn param(self) -> ParamValue { + ParamValue::Owned(PCWSTR(self.as_ptr())) + } +} + +impl Param for PWSTR { + unsafe fn param(self) -> ParamValue { + ParamValue::Owned(PCWSTR(self.0)) + } +} + +impl Param for PSTR { + unsafe fn param(self) -> ParamValue { + ParamValue::Owned(PCSTR(self.0)) + } +} + +impl RuntimeType for HSTRING { + const SIGNATURE: imp::ConstBuffer = imp::ConstBuffer::from_slice(b"string"); + const NAME: imp::ConstBuffer = imp::ConstBuffer::from_slice(b"String"); +} + +impl TypeKind for PWSTR { + type TypeKind = CopyType; +} + +impl TypeKind for PSTR { + type TypeKind = CopyType; +} + +impl TypeKind for PCWSTR { + type TypeKind = CopyType; +} + +impl TypeKind for PCSTR { + type TypeKind = CopyType; +} + +impl TypeKind for HSTRING { + type TypeKind = CloneType; +} diff --git a/third_party/windows-winui/windows-core/src/out_param.rs b/third_party/windows-winui/windows-core/src/out_param.rs new file mode 100644 index 0000000000..828746c1f7 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/out_param.rs @@ -0,0 +1,63 @@ +use super::*; +use core::mem::{take, transmute_copy, zeroed}; + +/// Provides automatic parameter conversion in cases where the Windows API expects implicit conversion support. +/// +/// This is a mutable version of [Param] meant to support out parameters. +/// There is no need to implement this trait. Blanket implementations are provided for all applicable Windows types. +pub trait OutParam::TypeKind>: Sized +where + T: Type, +{ + #[doc(hidden)] + unsafe fn borrow_mut(&self) -> OutRef<'_, T>; +} + +impl OutParam for &mut T +where + T: TypeKind + Clone + Default, +{ + unsafe fn borrow_mut(&self) -> OutRef<'_, T> { + unsafe { + let this: &mut T = transmute_copy(self); + take(this); + transmute_copy(self) + } + } +} + +impl OutParam for &mut T +where + T: TypeKind + Clone + Default, +{ + unsafe fn borrow_mut(&self) -> OutRef<'_, T> { + unsafe { transmute_copy(self) } + } +} + +impl OutParam for &mut Option +where + T: TypeKind + Clone, +{ + unsafe fn borrow_mut(&self) -> OutRef<'_, T> { + unsafe { + let this: &mut Option = transmute_copy(self); + take(this); + transmute_copy(self) + } + } +} + +impl OutParam for Option<&mut T> +where + T: Type, +{ + unsafe fn borrow_mut(&self) -> OutRef<'_, T> { + unsafe { + match self { + Some(this) => transmute_copy(this), + None => zeroed(), + } + } + } +} diff --git a/third_party/windows-winui/windows-core/src/out_ref.rs b/third_party/windows-winui/windows-core/src/out_ref.rs new file mode 100644 index 0000000000..d82e61dad4 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/out_ref.rs @@ -0,0 +1,37 @@ +use super::*; + +/// A borrowed type with the same memory layout as the type itself that can be used to construct ABI-compatible function signatures. +/// +/// This is a mutable version of [Ref] meant to support out parameters. +#[repr(transparent)] +pub struct OutRef<'a, T: Type>(*mut T::Abi, core::marker::PhantomData<&'a T>); + +impl> OutRef<'_, T> { + /// Returns `true` if the argument is null. + pub fn is_null(&self) -> bool { + self.0.is_null() + } + + /// Overwrites a memory location with the given value without reading or dropping the old value. + pub fn write(self, value: T::Default) -> Result<()> { + if self.0.is_null() { + Err(Error::from_hresult(imp::E_POINTER)) + } else { + unsafe { *self.0 = core::mem::transmute_copy(&value) } + core::mem::forget(value); + Ok(()) + } + } +} + +impl<'a, T: Type> From<&'a mut T::Default> for OutRef<'a, T> { + fn from(from: &'a mut T::Default) -> Self { + unsafe { core::mem::transmute(from) } + } +} + +impl> Default for OutRef<'_, T> { + fn default() -> Self { + OutRef(core::ptr::null_mut(), core::marker::PhantomData) + } +} diff --git a/third_party/windows-winui/windows-core/src/param.rs b/third_party/windows-winui/windows-core/src/param.rs new file mode 100644 index 0000000000..5ebf3add08 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/param.rs @@ -0,0 +1,76 @@ +use super::*; +use core::mem::transmute_copy; +use core::mem::zeroed; + +/// Provides automatic parameter conversion in cases where the Windows API expects implicit conversion support. +/// +/// There is no need to implement this trait. Blanket implementations are provided for all applicable Windows types. +pub trait Param::TypeKind>: Sized +where + T: Type, +{ + #[doc(hidden)] + unsafe fn param(self) -> ParamValue; +} + +impl Param for Option<&T> +where + T: Type, +{ + unsafe fn param(self) -> ParamValue { + unsafe { + ParamValue::Borrowed(match self { + Some(item) => transmute_copy(item), + None => zeroed(), + }) + } + } +} + +impl Param for InterfaceRef<'_, T> +where + T: Type, +{ + unsafe fn param(self) -> ParamValue { + unsafe { ParamValue::Borrowed(transmute_copy(&self)) } + } +} + +impl Param for &U +where + T: TypeKind + Clone, + T: Interface, + U: Interface, + U: imp::CanInto, +{ + unsafe fn param(self) -> ParamValue { + unsafe { + if U::QUERY { + self.cast() + .map_or(ParamValue::Borrowed(zeroed()), |ok| ParamValue::Owned(ok)) + } else { + ParamValue::Borrowed(transmute_copy(self)) + } + } + } +} + +impl Param for &T +where + T: TypeKind + Clone, +{ + unsafe fn param(self) -> ParamValue { + unsafe { ParamValue::Borrowed(transmute_copy(self)) } + } +} + +impl Param for U +where + T: TypeKind + Clone, + U: TypeKind + Clone, + U: imp::CanInto, +{ + unsafe fn param(self) -> ParamValue { + unsafe { ParamValue::Owned(transmute_copy(&self)) } + } +} diff --git a/third_party/windows-winui/windows-core/src/param_value.rs b/third_party/windows-winui/windows-core/src/param_value.rs new file mode 100644 index 0000000000..4500e261e8 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/param_value.rs @@ -0,0 +1,24 @@ +use super::*; +use core::mem::transmute_copy; + +#[doc(hidden)] +pub enum ParamValue> { + Owned(T), + Borrowed(T::Abi), +} + +impl> ParamValue { + // TODO: replace with `borrow` in windows-bindgen + pub fn abi(&self) -> T::Abi { + unsafe { + match self { + Self::Owned(item) => transmute_copy(item), + Self::Borrowed(borrowed) => transmute_copy(borrowed), + } + } + } + + pub fn borrow(&self) -> Ref<'_, T> { + unsafe { transmute_copy(&self.abi()) } + } +} diff --git a/third_party/windows-winui/windows-core/src/resources.rs b/third_party/windows-winui/windows-core/src/resources.rs new file mode 100644 index 0000000000..239af2b15d --- /dev/null +++ b/third_party/windows-winui/windows-core/src/resources.rs @@ -0,0 +1,54 @@ +/// Custom code to free a resource. +/// +/// This is similar to the [`Drop`] trait, and may be used to implement [`Drop`], but allows resources +/// to be freed depending on context. +pub trait Free : Clone { + /// Calls the resource's free function. + /// + /// # Safety + /// The resource must be owned by the caller and safe to free. + unsafe fn free(&mut self); +} + +/// A wrapper to provide ownership for resources to automatically drop via the resource's [`Free`] trait. +#[repr(transparent)] +#[derive(PartialEq, Eq, Default, Debug)] +pub struct Owned(T); + +impl Owned { + /// Takes ownership of the resource. + /// + /// # Safety + /// + /// The resource must be owned by the caller and safe to free. + pub unsafe fn new(x: T) -> Self { + Self(x) + } + + /// Consumes the `Owned` and relinquishes ownership of the resource. + /// The caller is now responsible for freeing the returned resource. + #[must_use = "losing the resource will leak it"] + pub fn into_raw(o: Self) -> T { + let o = core::mem::ManuallyDrop::new(o); + o.0.clone() + } +} + +impl Drop for Owned { + fn drop(&mut self) { + unsafe { self.0.free() }; + } +} + +impl core::ops::Deref for Owned { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl core::ops::DerefMut for Owned { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} diff --git a/third_party/windows-winui/windows-core/src/runtime_name.rs b/third_party/windows-winui/windows-core/src/runtime_name.rs new file mode 100644 index 0000000000..b1420785a5 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/runtime_name.rs @@ -0,0 +1,8 @@ +use super::*; + +#[doc(hidden)] +pub trait RuntimeName { + const NAME: &'static str = ""; + const RUNTIME_CLASS_NAME: imp::ConstBuffer = + imp::ConstBuffer::from_slice(Self::NAME.as_bytes()); +} diff --git a/third_party/windows-winui/windows-core/src/runtime_type.rs b/third_party/windows-winui/windows-core/src/runtime_type.rs new file mode 100644 index 0000000000..767dd77894 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/runtime_type.rs @@ -0,0 +1,32 @@ +use super::*; + +#[doc(hidden)] +pub trait RuntimeType: Type { + const SIGNATURE: imp::ConstBuffer; + const NAME: imp::ConstBuffer = imp::ConstBuffer::new(); +} + +macro_rules! primitives { + ($(($t:ty, $s:literal, $name:literal)),+) => { + $( + impl RuntimeType for $t { + const SIGNATURE: imp::ConstBuffer = imp::ConstBuffer::from_slice($s); + const NAME: imp::ConstBuffer = imp::ConstBuffer::from_slice($name); + } + )* + }; +} + +primitives! { + (bool, b"b1", b"Boolean"), + (i8, b"i1", b"Int8"), + (u8, b"u1", b"UInt8"), + (i16, b"i2", b"Int16"), + (u16, b"u2", b"UInt16"), + (i32, b"i4", b"Int32"), + (u32, b"u4", b"UInt32"), + (i64, b"i8", b"Int64"), + (u64, b"u8", b"UInt64"), + (f32, b"f4", b"Single"), + (f64, b"f8", b"Double") +} diff --git a/third_party/windows-winui/windows-core/src/scoped_interface.rs b/third_party/windows-winui/windows-core/src/scoped_interface.rs new file mode 100644 index 0000000000..5a701e8cee --- /dev/null +++ b/third_party/windows-winui/windows-core/src/scoped_interface.rs @@ -0,0 +1,41 @@ +use super::*; +use core::ffi::c_void; +use core::marker::PhantomData; + +#[doc(hidden)] +#[repr(C)] +pub struct ScopedHeap { + pub vtable: *const c_void, + pub this: *const c_void, +} + +#[doc(hidden)] +pub struct ScopedInterface<'a, T: Interface> { + interface: T, + lifetime: PhantomData<&'a T>, +} + +impl ScopedInterface<'_, T> { + pub fn new(interface: T) -> Self { + Self { + interface, + lifetime: PhantomData, + } + } +} + +impl core::ops::Deref for ScopedInterface<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + &self.interface + } +} + +impl Drop for ScopedInterface<'_, T> { + fn drop(&mut self) { + unsafe { + let _ = Box::from_raw(self.interface.as_raw() as *const _ as *mut ScopedHeap); + } + } +} diff --git a/third_party/windows-winui/windows-core/src/type.rs b/third_party/windows-winui/windows-core/src/type.rs new file mode 100644 index 0000000000..eb18bc9150 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/type.rs @@ -0,0 +1,181 @@ +use super::*; + +#[doc(hidden)] +pub trait TypeKind { + type TypeKind; +} + +#[doc(hidden)] +pub struct InterfaceType; + +#[doc(hidden)] +pub struct CloneType; + +#[doc(hidden)] +pub struct CopyType; + +#[doc(hidden)] +pub trait Type::TypeKind>: TypeKind + Sized + Clone { + type Abi; + type Default; + + /// The parameter type used in `_Impl` trait method signatures. + /// + /// For `CopyType` (primitives, enums, GUID, etc.) this is `T` directly — the type itself, + /// since these are cheap to copy and `InRef` adds no value. For `CloneType` (HSTRING, etc.) + /// and `InterfaceType` (COM interfaces) this is `InRef<'a, T>` — a borrowed wrapper that + /// matches the ABI representation. + type Ref<'a>: 'a + where + Self: 'a; + + fn is_null(abi: &Self::Abi) -> bool; + unsafe fn assume_init_ref(abi: &Self::Abi) -> &Self; + unsafe fn from_abi(abi: Self::Abi) -> Result; + fn from_default(default: &Self::Default) -> Result; +} + +impl Type for T +where + T: TypeKind + Clone, +{ + type Abi = *mut core::ffi::c_void; + type Default = Option; + type Ref<'a> + = InRef<'a, Self> + where + Self: 'a; + + fn is_null(abi: &Self::Abi) -> bool { + abi.is_null() + } + + unsafe fn assume_init_ref(abi: &Self::Abi) -> &Self { + unsafe { core::mem::transmute::<&*mut core::ffi::c_void, &T>(abi) } + } + + unsafe fn from_abi(abi: Self::Abi) -> Result { + unsafe { + if !abi.is_null() { + Ok(core::mem::transmute_copy(&abi)) + } else { + Err(Error::empty()) + } + } + } + + fn from_default(default: &Self::Default) -> Result { + default.as_ref().cloned().ok_or(Error::empty()) + } +} + +impl Type for T +where + T: TypeKind + Clone, +{ + type Abi = core::mem::MaybeUninit; + type Default = Self; + type Ref<'a> + = InRef<'a, Self> + where + Self: 'a; + + fn is_null(_: &Self::Abi) -> bool { + false + } + + unsafe fn assume_init_ref(abi: &Self::Abi) -> &Self { + unsafe { abi.assume_init_ref() } + } + + unsafe fn from_abi(abi: Self::Abi) -> Result { + unsafe { Ok(abi.assume_init()) } + } + + fn from_default(default: &Self::Default) -> Result { + Ok(default.clone()) + } +} + +impl Type for T +where + T: TypeKind + Clone, +{ + type Abi = Self; + type Default = Self; + type Ref<'a> + = Self + where + Self: 'a; + + fn is_null(_: &Self::Abi) -> bool { + false + } + + unsafe fn assume_init_ref(abi: &Self::Abi) -> &Self { + abi + } + + unsafe fn from_abi(abi: Self::Abi) -> Result { + Ok(abi) + } + + fn from_default(default: &Self) -> Result { + Ok(default.clone()) + } +} + +impl TypeKind for T { + type TypeKind = InterfaceType; +} + +impl TypeKind for *mut T { + type TypeKind = CopyType; +} + +macro_rules! primitives { + ($($t:ty),+) => { + $( + impl TypeKind for $t { + type TypeKind = CopyType; + } + )* + }; +} + +primitives!(bool, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, usize, isize); + +#[doc(hidden)] +pub type AbiType = >::Abi; + +/// The parameter type for a generic type `T` used in `_Impl` trait method signatures. +/// +/// For `CopyType` (primitives, enums, GUID, etc.) this resolves to `T` directly — the type +/// itself, since these are cheap to copy and no wrapper is needed. For all other types +/// (`CloneType` such as `HSTRING`, and `InterfaceType` such as COM interfaces) this resolves +/// to [`Ref<'a, T>`], a borrowed wrapper that matches the ABI layout. +/// +/// # Example +/// +/// ```rust,ignore +/// impl IMap_Impl for MyMap_Impl { +/// fn HasKey(&self, key: Ref) -> Result { +/// // key is just i32 — no InRef wrapper needed +/// Ok(self.map.contains_key(&key)) +/// } +/// } +/// ``` +pub type Ref<'a, T> = >::Ref<'a>; + +/// Converts a [`Ref`](Ref) reference into a reference to the corresponding +/// [`Default`](Type::Default) representation. +/// +/// `Ref` and `T::Default` always share the same memory layout, so this is a +/// zero-cost reinterpretation: +/// - For `CopyType` (primitives, GUID, enums): `Ref<'_> = T = Default`, no-op. +/// - For `CloneType` (HSTRING, etc.) and `InterfaceType` (COM interfaces): +/// `Ref<'_> = InRef<'_, T>`, which is `#[repr(transparent)]` over `T::Abi`, and +/// `T::Abi` has the same layout as `T::Default`. +pub fn ref_as_default<'a, 'b, T: Type>(param: &'a >::Ref<'b>) -> &'a T::Default { + unsafe { &*(param as *const >::Ref<'b> as *const T::Default) } +} diff --git a/third_party/windows-winui/windows-core/src/unknown.rs b/third_party/windows-winui/windows-core/src/unknown.rs new file mode 100644 index 0000000000..9dda1c309f --- /dev/null +++ b/third_party/windows-winui/windows-core/src/unknown.rs @@ -0,0 +1,186 @@ +use super::*; +use core::ffi::c_void; +use core::ptr::NonNull; + +/// Base interface for all COM interfaces. +/// +/// All COM interfaces (and thus WinRT classes and interfaces) implement +/// [IUnknown](https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown) +/// under the hood to provide reference-counted lifetime management as well as the ability +/// to query for additional interfaces that the object may implement. +#[repr(transparent)] +pub struct IUnknown(NonNull); + +#[doc(hidden)] +#[repr(C)] +pub struct IUnknown_Vtbl { + pub QueryInterface: unsafe extern "system" fn( + this: *mut c_void, + iid: *const GUID, + interface: *mut *mut c_void, + ) -> HRESULT, + pub AddRef: unsafe extern "system" fn(this: *mut c_void) -> u32, + pub Release: unsafe extern "system" fn(this: *mut c_void) -> u32, +} + +unsafe impl Interface for IUnknown { + type Vtable = IUnknown_Vtbl; + const IID: GUID = GUID::from_u128(0x00000000_0000_0000_c000_000000000046); +} + +impl Clone for IUnknown { + fn clone(&self) -> Self { + unsafe { + (self.vtable().AddRef)(self.as_raw()); + } + + Self(self.0) + } +} + +impl Drop for IUnknown { + fn drop(&mut self) { + unsafe { + (self.vtable().Release)(self.as_raw()); + } + } +} + +impl PartialEq for IUnknown { + fn eq(&self, other: &Self) -> bool { + // First we test for ordinary pointer equality. If two COM interface pointers have the + // same pointer value, then they are the same object. This can save us a lot of time, + // since calling QueryInterface is much more expensive than a single pointer comparison. + // + // However, interface pointers may have different values and yet point to the same object. + // Since COM objects may implement multiple interfaces, COM identity can only + // be determined by querying for `IUnknown` explicitly and then comparing the + // pointer values. This works since `QueryInterface` is required to return + // the same pointer value for queries for `IUnknown`. + core::ptr::eq(self.as_raw(), other.as_raw()) + || self.cast::().unwrap().0 == other.cast::().unwrap().0 + } +} + +impl Eq for IUnknown {} + +impl core::fmt::Debug for IUnknown { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_tuple("IUnknown").field(&self.as_raw()).finish() + } +} + +/// The `#[implement]` macro generates implementations of this trait for the types +/// that it generates, e.g. `MyApp_Impl`, +/// +/// `ComObject` uses this trait to interact with boxed COM objects. +#[doc(hidden)] +pub trait IUnknownImpl { + /// The contained user type, e.g. `MyApp`. Also known as the "inner" type. + type Impl; + + /// Get a reference to the backing implementation. + fn get_impl(&self) -> &Self::Impl; + + /// Get a mutable reference to the contained (inner) object. + fn get_impl_mut(&mut self) -> &mut Self::Impl; + + /// Consumes the box and returns the contained (inner) object. This is the opposite of `new_box`. + fn into_inner(self) -> Self::Impl; + + /// The classic `QueryInterface` method from COM. + /// + /// # Safety + /// + /// This function is safe to call as long as the interface pointer is non-null and valid for writes + /// of an interface pointer. + unsafe fn QueryInterface(&self, iid: *const GUID, interface: *mut *mut c_void) -> HRESULT; + + /// Increments the reference count of the interface + fn AddRef(&self) -> u32; + + /// Decrements the reference count causing the interface's memory to be freed when the count is 0 + /// + /// # Safety + /// + /// This function should only be called when the interface pointer is no longer used as calling `Release` + /// on a non-aliased interface pointer and then using that interface pointer may result in use after free. + /// + /// This function takes `*mut Self` because the object may be freed by the time this method returns. + /// Taking `&self` would violate Rust's rules on reference lifetime. + unsafe fn Release(self_: *mut Self) -> u32; + + /// Returns `true` if the reference count of the box is equal to 1. + fn is_reference_count_one(&self) -> bool; + + /// Gets the trust level of the current object. + unsafe fn GetTrustLevel(&self, value: *mut i32) -> HRESULT; + + /// Gets a borrowed reference to an interface that is implemented by this ComObject. + /// + /// The returned reference does not have an additional reference count. + /// You can AddRef it by calling to_owned(). + #[inline(always)] + fn as_interface(&self) -> InterfaceRef<'_, I> + where + Self: ComObjectInterface, + { + >::as_interface_ref(self) + } + + /// Gets an owned (counted) reference to an interface that is implemented by this ComObject. + #[inline(always)] + fn to_interface(&self) -> I + where + Self: ComObjectInterface, + { + >::as_interface_ref(self).to_owned() + } + + /// Creates a new owned reference to this object. + /// + /// # Safety + /// + /// This function can only be safely called by `_Impl` objects that are embedded in a + /// `ComObject`. Since we only allow safe Rust code to access these objects using a `ComObject` + /// or a `&_Impl` that points within a `ComObject`, this is safe. + fn to_object(&self) -> ComObject + where + Self::Impl: ComObjectInner; +} + +impl IUnknown_Vtbl { + pub const fn new() -> Self { + unsafe extern "system" fn QueryInterface( + this: *mut c_void, + iid: *const GUID, + interface: *mut *mut c_void, + ) -> HRESULT { + unsafe { + let this = (this as *mut *mut c_void).offset(OFFSET) as *mut T; + (*this).QueryInterface(iid, interface) + } + } + unsafe extern "system" fn AddRef( + this: *mut c_void, + ) -> u32 { + unsafe { + let this = (this as *mut *mut c_void).offset(OFFSET) as *mut T; + (*this).AddRef() + } + } + unsafe extern "system" fn Release( + this: *mut c_void, + ) -> u32 { + unsafe { + let this = (this as *mut *mut c_void).offset(OFFSET) as *mut T; + T::Release(this) + } + } + Self { + QueryInterface: QueryInterface::, + AddRef: AddRef::, + Release: Release::, + } + } +} diff --git a/third_party/windows-winui/windows-core/src/weak.rs b/third_party/windows-winui/windows-core/src/weak.rs new file mode 100644 index 0000000000..b110ffbbf1 --- /dev/null +++ b/third_party/windows-winui/windows-core/src/weak.rs @@ -0,0 +1,28 @@ +use super::*; +use core::marker::PhantomData; + +/// `Weak` holds a non-owning reference to an object. +#[derive(Clone, PartialEq, Eq, Default)] +pub struct Weak(Option, PhantomData); + +impl Weak { + /// Creates a new `Weak` object without any backing object. + pub const fn new() -> Self { + Self(None, PhantomData) + } + + /// Attempts to upgrade the weak reference to a strong reference. + pub fn upgrade(&self) -> Option { + self.0 + .as_ref() + .and_then(|inner| unsafe { inner.Resolve().ok() }) + } + + pub(crate) fn downgrade(source: &imp::IWeakReferenceSource) -> Self { + let reference = unsafe { source.GetWeakReference().ok() }; + Self(reference, PhantomData) + } +} + +unsafe impl Send for Weak {} +unsafe impl Sync for Weak {} diff --git a/third_party/windows-winui/windows-core/src/windows.rs b/third_party/windows-winui/windows-core/src/windows.rs new file mode 100644 index 0000000000..348cff7a0b --- /dev/null +++ b/third_party/windows-winui/windows-core/src/windows.rs @@ -0,0 +1,15 @@ +mod array; +pub use array::*; + +mod resources; +pub use resources::*; + +impl Param for &BSTR { + unsafe fn param(self) -> ParamValue { + ParamValue::Owned(PCWSTR(self.as_ptr())) + } +} + +impl TypeKind for BSTR { + type TypeKind = CloneType; +} diff --git a/third_party/windows-winui/windows-core/windows-core.natvis b/third_party/windows-winui/windows-core/windows-core.natvis new file mode 100644 index 0000000000..192804a511 --- /dev/null +++ b/third_party/windows-winui/windows-core/windows-core.natvis @@ -0,0 +1,18 @@ + + + + {{ len={len} }} + + + len + + len + data + + + + + + {__0} + + diff --git a/third_party/windows-winui/windows-future/Cargo.toml b/third_party/windows-winui/windows-future/Cargo.toml new file mode 100644 index 0000000000..248b91075c --- /dev/null +++ b/third_party/windows-winui/windows-future/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "windows-future" +version = "0.3.2" +edition = "2021" +rust-version = "1.82" +license = "MIT OR Apache-2.0" +description = "Windows async types" +repository = "https://github.com/microsoft/windows-rs" +categories = ["os::windows-apis"] +readme = "readme.md" + +[dependencies] +windows-core = { path = "../windows-core", default-features = false } +windows-link = { path = "../windows-link" } +windows-threading = { path = "../windows-threading" } + +[features] +default = ["std"] +std = ["windows-core/std", "windows-threading/std"] + +[package.metadata.docs.rs] +targets = [] diff --git a/third_party/windows-winui/windows-future/license-apache-2.0 b/third_party/windows-winui/windows-future/license-apache-2.0 new file mode 100644 index 0000000000..b5ed4ecec2 --- /dev/null +++ b/third_party/windows-winui/windows-future/license-apache-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/windows-winui/windows-future/license-mit b/third_party/windows-winui/windows-future/license-mit new file mode 100644 index 0000000000..9e841e7a26 --- /dev/null +++ b/third_party/windows-winui/windows-future/license-mit @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/third_party/windows-winui/windows-future/readme.md b/third_party/windows-winui/windows-future/readme.md new file mode 100644 index 0000000000..43d5686a21 --- /dev/null +++ b/third_party/windows-winui/windows-future/readme.md @@ -0,0 +1,31 @@ +## Windows async types + +The [windows-future](https://crates.io/crates/windows-future) crate provides stock async support for Windows APIs. + +* [Getting started](https://kennykerr.ca/rust-getting-started/) +* [Samples](https://github.com/microsoft/windows-rs/tree/master/crates/samples) +* [Releases](https://github.com/microsoft/windows-rs/releases) + +Start by adding the following to your Cargo.toml file: + +```toml +[dependencies.windows-future] +version = "0.3" +``` + +Use the Windows async types as needed: + +```rust,ignore +use windows_future::*; + +// This result will be available immediately. +let ready = IAsyncOperation::ready(Ok(123)); +assert_eq!(ready.join().unwrap(), 123); + +let ready = IAsyncOperation::spawn(|| { + // Some lengthy operation goes here... + Ok(456) +}); + +assert_eq!(ready.join().unwrap(), 456); +``` diff --git a/third_party/windows-winui/windows-future/src/async.rs b/third_party/windows-winui/windows-future/src/async.rs new file mode 100644 index 0000000000..1a50e78e00 --- /dev/null +++ b/third_party/windows-winui/windows-future/src/async.rs @@ -0,0 +1,170 @@ +use super::*; + +// An `Async` represents a WinRT async execution object or type. There are precisely four such types: +// - IAsyncAction +// - IAsyncActionWithProgress +// - IAsyncOperation +// - IAsyncOperationWithProgress +// +// All four implementations are provided here and there is thus no need to implement this trait. +// This trait provides an abstraction over the relevant differences so that the various async +// capabilities in this crate can be reused for all implementations. +pub trait Async: Interface { + // The type of value produced on completion. + type Output: Clone; + + // The type of the delegate use for completion notification. + type CompletedHandler: Interface; + + // Sets the handler or callback to invoke when execution completes. This handler can only be set once. + fn set_completed(&self, handler: F) -> Result<()>; + + // Calls the given handler with the current object and status. + #[cfg(feature = "std")] + fn invoke_completed(&self, handler: &Self::CompletedHandler, status: AsyncStatus); + + // Returns the value produced on completion. This should only be called when execution completes. + fn get_results(&self) -> Result; + + // Gets the current status of async execution. This calls `QueryInterface` so should be used sparingly. + fn status(&self) -> Result; + + // Waits for the async execution to finish and then returns the results. + fn join(&self) -> Result { + if self.status()? == AsyncStatus::Started { + let (waiter, signaler) = Waiter::new()?; + self.set_completed(move |_| { + // This is safe because the waiter will only be dropped after being signaled. + unsafe { + signaler.signal(); + } + })?; + waiter.wait(); + } + self.get_results() + } + + // Calls `op(result)` when async execution completes. + fn when(&self, op: F) -> Result<()> + where + F: FnOnce(Result) + Send + 'static, + { + if self.status()? == AsyncStatus::Started { + // The `set_completed` closure is guaranteed to only be called once, like `FnOnce`, by the async pattern, + // but Rust doesn't know that so `RefCell` is used to pass `op` in to the closure. + let op = core::cell::RefCell::new(Some(op)); + self.set_completed(move |sender| { + if let Some(op) = op.take() { + op(sender.get_results()); + } + })?; + } else { + op(self.get_results()); + } + Ok(()) + } +} + +impl Async for IAsyncAction { + type Output = (); + type CompletedHandler = AsyncActionCompletedHandler; + + fn set_completed(&self, handler: F) -> Result<()> { + self.SetCompleted(&AsyncActionCompletedHandler::new(move |sender, _| { + handler(sender.ok()?); + Ok(()) + })) + } + + #[cfg(feature = "std")] + fn invoke_completed(&self, handler: &Self::CompletedHandler, status: AsyncStatus) { + _ = handler.Invoke(self, status); + } + + fn get_results(&self) -> Result { + self.GetResults() + } + + fn status(&self) -> Result { + self.Status() + } +} + +impl Async for IAsyncOperation { + type Output = T; + type CompletedHandler = AsyncOperationCompletedHandler; + + fn set_completed(&self, handler: F) -> Result<()> { + self.SetCompleted(&AsyncOperationCompletedHandler::new(move |sender, _| { + handler(sender.ok()?); + Ok(()) + })) + } + + #[cfg(feature = "std")] + fn invoke_completed(&self, handler: &Self::CompletedHandler, status: AsyncStatus) { + _ = handler.Invoke(self, status); + } + + fn get_results(&self) -> Result { + self.GetResults() + } + + fn status(&self) -> Result { + self.Status() + } +} + +impl Async for IAsyncActionWithProgress

{ + type Output = (); + type CompletedHandler = AsyncActionWithProgressCompletedHandler

; + + fn set_completed(&self, handler: F) -> Result<()> { + self.SetCompleted(&AsyncActionWithProgressCompletedHandler::new( + move |sender, _| { + handler(sender.ok()?); + Ok(()) + }, + )) + } + + #[cfg(feature = "std")] + fn invoke_completed(&self, handler: &Self::CompletedHandler, status: AsyncStatus) { + _ = handler.Invoke(self, status); + } + + fn get_results(&self) -> Result { + self.GetResults() + } + + fn status(&self) -> Result { + self.Status() + } +} + +impl Async for IAsyncOperationWithProgress { + type Output = T; + type CompletedHandler = AsyncOperationWithProgressCompletedHandler; + + fn set_completed(&self, handler: F) -> Result<()> { + self.SetCompleted(&AsyncOperationWithProgressCompletedHandler::new( + move |sender, _| { + handler(sender.ok()?); + Ok(()) + }, + )) + } + + #[cfg(feature = "std")] + fn invoke_completed(&self, handler: &Self::CompletedHandler, status: AsyncStatus) { + _ = handler.Invoke(self, status); + } + + fn get_results(&self) -> Result { + self.GetResults() + } + + fn status(&self) -> Result { + self.Status() + } +} diff --git a/third_party/windows-winui/windows-future/src/async_ready.rs b/third_party/windows-winui/windows-future/src/async_ready.rs new file mode 100644 index 0000000000..3fdb63b938 --- /dev/null +++ b/third_party/windows-winui/windows-future/src/async_ready.rs @@ -0,0 +1,252 @@ +use super::*; +use std::sync::atomic::{AtomicBool, Ordering}; + +struct ReadyState { + set_completed: AtomicBool, + result: Result, +} + +impl ReadyState { + fn new(result: Result) -> Self { + Self { + set_completed: AtomicBool::new(false), + result, + } + } + + fn status(&self) -> AsyncStatus { + if self.result.is_ok() { + AsyncStatus::Completed + } else { + AsyncStatus::Error + } + } + + // The "Ready" implementations don't need to store the handler since the handler is invoked immediately + // but still need to confirm that `SetCompleted` is called at most once. + fn invoke_completed(&self, sender: &T, handler: Ref) -> Result<()> { + if !self.set_completed.swap(true, Ordering::SeqCst) { + sender.invoke_completed(handler.ok()?, self.status()); + Ok(()) + } else { + Err(Error::from_hresult(HRESULT(0x80000018u32 as i32))) // E_ILLEGAL_DELEGATE_ASSIGNMENT + } + } + + // The `From` implementation is not used here since we don't want to transfer any error object to the calling thread. + // That happens when `GetResults` is called. + fn error_code(&self) -> HRESULT { + match &self.result { + Ok(_) => HRESULT(0), + Err(error) => error.code(), + } + } +} + +struct ReadyAction(ReadyState); + +implement_decl! { + impl ReadyAction as ReadyAction_Impl: [IAsyncAction, IAsyncInfo] +} + +struct ReadyOperation(ReadyState>) +where + T: RuntimeType + 'static; + +implement_decl! { + impl ReadyOperation as ReadyOperation_Impl: [ + IAsyncOperation, + IAsyncInfo, + ] + where T: RuntimeType + 'static +} + +struct ReadyActionWithProgress

(ReadyState>) +where + P: RuntimeType + 'static; + +implement_decl! { + impl

ReadyActionWithProgress as ReadyActionWithProgress_Impl: [ + IAsyncActionWithProgress

, + IAsyncInfo, + ] + where P: RuntimeType + 'static +} + +struct ReadyOperationWithProgress(ReadyState>) +where + T: RuntimeType + 'static, + P: RuntimeType + 'static; + +implement_decl! { + impl ReadyOperationWithProgress as ReadyOperationWithProgress_Impl: [ + IAsyncOperationWithProgress, + IAsyncInfo, + ] + where T: RuntimeType + 'static, P: RuntimeType + 'static +} + +impl IAsyncInfo_Impl for ReadyAction_Impl { + fn Id(&self) -> Result { + Ok(1) + } + fn Status(&self) -> Result { + Ok(self.0.status()) + } + fn ErrorCode(&self) -> Result { + Ok(self.0.error_code()) + } + fn Cancel(&self) -> Result<()> { + Ok(()) + } + fn Close(&self) -> Result<()> { + Ok(()) + } +} + +impl IAsyncInfo_Impl for ReadyOperation_Impl { + fn Id(&self) -> Result { + Ok(1) + } + fn Status(&self) -> Result { + Ok(self.0.status()) + } + fn ErrorCode(&self) -> Result { + Ok(self.0.error_code()) + } + fn Cancel(&self) -> Result<()> { + Ok(()) + } + fn Close(&self) -> Result<()> { + Ok(()) + } +} + +impl IAsyncInfo_Impl for ReadyActionWithProgress_Impl

{ + fn Id(&self) -> Result { + Ok(1) + } + fn Status(&self) -> Result { + Ok(self.0.status()) + } + fn ErrorCode(&self) -> Result { + Ok(self.0.error_code()) + } + fn Cancel(&self) -> Result<()> { + Ok(()) + } + fn Close(&self) -> Result<()> { + Ok(()) + } +} + +impl IAsyncInfo_Impl for ReadyOperationWithProgress_Impl { + fn Id(&self) -> Result { + Ok(1) + } + fn Status(&self) -> Result { + Ok(self.0.status()) + } + fn ErrorCode(&self) -> Result { + Ok(self.0.error_code()) + } + fn Cancel(&self) -> Result<()> { + Ok(()) + } + fn Close(&self) -> Result<()> { + Ok(()) + } +} + +impl IAsyncAction_Impl for ReadyAction_Impl { + fn SetCompleted(&self, handler: Ref) -> Result<()> { + self.0.invoke_completed(&self.as_interface(), handler) + } + fn Completed(&self) -> Result { + Err(Error::empty()) + } + fn GetResults(&self) -> Result<()> { + self.0.result.clone() + } +} + +impl IAsyncOperation_Impl for ReadyOperation_Impl { + fn SetCompleted(&self, handler: Ref>) -> Result<()> { + self.0.invoke_completed(&self.as_interface(), handler) + } + fn Completed(&self) -> Result> { + Err(Error::empty()) + } + fn GetResults(&self) -> Result { + self.0.result.clone() + } +} + +impl IAsyncActionWithProgress_Impl

for ReadyActionWithProgress_Impl

{ + fn SetCompleted(&self, handler: Ref>) -> Result<()> { + self.0.invoke_completed(&self.as_interface(), handler) + } + fn Completed(&self) -> Result> { + Err(Error::empty()) + } + fn GetResults(&self) -> Result<()> { + self.0.result.clone() + } + fn SetProgress(&self, _: Ref>) -> Result<()> { + Ok(()) + } + fn Progress(&self) -> Result> { + Err(Error::empty()) + } +} + +impl IAsyncOperationWithProgress_Impl + for ReadyOperationWithProgress_Impl +{ + fn SetCompleted( + &self, + handler: Ref>, + ) -> Result<()> { + self.0.invoke_completed(&self.as_interface(), handler) + } + fn Completed(&self) -> Result> { + Err(Error::empty()) + } + fn GetResults(&self) -> Result { + self.0.result.clone() + } + fn SetProgress(&self, _: Ref>) -> Result<()> { + Ok(()) + } + fn Progress(&self) -> Result> { + Err(Error::empty()) + } +} + +impl IAsyncAction { + /// Creates an `IAsyncAction` that is immediately ready with a value. + pub fn ready(result: Result<()>) -> Self { + ReadyAction(ReadyState::new(result)).into() + } +} + +impl IAsyncOperation { + /// Creates an `IAsyncOperation` that is immediately ready with a value. + pub fn ready(result: Result) -> Self { + ReadyOperation(ReadyState::new(result)).into() + } +} + +impl IAsyncActionWithProgress

{ + /// Creates an `IAsyncActionWithProgress

` that is immediately ready with a value. + pub fn ready(result: Result<()>) -> Self { + ReadyActionWithProgress(ReadyState::new(result)).into() + } +} + +impl IAsyncOperationWithProgress { + /// Creates an `IAsyncOperationWithProgress` that is immediately ready with a value. + pub fn ready(result: Result) -> Self { + ReadyOperationWithProgress(ReadyState::new(result)).into() + } +} diff --git a/third_party/windows-winui/windows-future/src/async_spawn.rs b/third_party/windows-winui/windows-future/src/async_spawn.rs new file mode 100644 index 0000000000..c7213c234f --- /dev/null +++ b/third_party/windows-winui/windows-future/src/async_spawn.rs @@ -0,0 +1,346 @@ +use super::*; +use std::sync::Mutex; + +struct State { + result: Option>, + completed: Option, + completed_assigned: bool, +} + +impl State { + fn status(&self) -> AsyncStatus { + match &self.result { + None => AsyncStatus::Started, + Some(Ok(_)) => AsyncStatus::Completed, + Some(Err(_)) => AsyncStatus::Error, + } + } + + fn error_code(&self) -> HRESULT { + match &self.result { + Some(Err(error)) => error.code(), + _ => HRESULT(0), + } + } + + fn get_results(&self) -> Result { + match &self.result { + Some(result) => result.clone(), + None => Err(Error::from_hresult(HRESULT(0x8000000Eu32 as i32))), // E_ILLEGAL_METHOD_CALL + } + } +} + +struct SyncState(Mutex>); + +impl SyncState { + fn new() -> Self { + Self(Mutex::new(State { + result: None, + completed: None, + completed_assigned: false, + })) + } + + fn status(&self) -> AsyncStatus { + self.0.lock().unwrap().status() + } + + fn error_code(&self) -> HRESULT { + self.0.lock().unwrap().error_code() + } + + fn get_results(&self) -> Result { + self.0.lock().unwrap().get_results() + } + + fn set_completed(&self, sender: &T, handler: Ref) -> Result<()> { + let mut guard = self.0.lock().unwrap(); + + if guard.completed_assigned { + Err(Error::from_hresult(HRESULT(0x80000018u32 as i32))) // E_ILLEGAL_DELEGATE_ASSIGNMENT + } else { + let status = guard.status(); + let handler = handler.ok()?; + + guard.completed_assigned = true; + + if status == AsyncStatus::Started { + guard.completed = Some(handler.clone()); + } else { + drop(guard); + sender.invoke_completed(handler, status); + } + + Ok(()) + } + } + + fn spawn(&self, sender: &T, f: F) + where + F: FnOnce() -> Result + Send + 'static, + { + let result = f(); + let mut guard = self.0.lock().unwrap(); + debug_assert!(guard.result.is_none()); + guard.result = Some(result); + let status = guard.status(); + let completed = guard.completed.take(); + + drop(guard); + + if let Some(completed) = completed { + sender.invoke_completed(&completed, status); + } + } +} + +unsafe impl Send for SyncState {} + +struct Action(SyncState); + +implement_decl! { + impl Action as Action_Impl: [IAsyncAction, IAsyncInfo] +} + +struct Operation(SyncState>) +where + T: RuntimeType + 'static; + +implement_decl! { + impl Operation as Operation_Impl: [ + IAsyncOperation, + IAsyncInfo, + ] + where T: RuntimeType + 'static +} + +struct ActionWithProgress

(SyncState>) +where + P: RuntimeType + 'static; + +implement_decl! { + impl

ActionWithProgress as ActionWithProgress_Impl: [ + IAsyncActionWithProgress

, + IAsyncInfo, + ] + where P: RuntimeType + 'static +} + +struct OperationWithProgress(SyncState>) +where + T: RuntimeType + 'static, + P: RuntimeType + 'static; + +implement_decl! { + impl OperationWithProgress as OperationWithProgress_Impl: [ + IAsyncOperationWithProgress, + IAsyncInfo, + ] + where T: RuntimeType + 'static, P: RuntimeType + 'static +} + +impl IAsyncInfo_Impl for Action_Impl { + fn Id(&self) -> Result { + Ok(1) + } + fn Status(&self) -> Result { + Ok(self.0.status()) + } + fn ErrorCode(&self) -> Result { + Ok(self.0.error_code()) + } + fn Cancel(&self) -> Result<()> { + Ok(()) + } + fn Close(&self) -> Result<()> { + Ok(()) + } +} + +impl IAsyncInfo_Impl for Operation_Impl { + fn Id(&self) -> Result { + Ok(1) + } + fn Status(&self) -> Result { + Ok(self.0.status()) + } + fn ErrorCode(&self) -> Result { + Ok(self.0.error_code()) + } + fn Cancel(&self) -> Result<()> { + Ok(()) + } + fn Close(&self) -> Result<()> { + Ok(()) + } +} + +impl IAsyncInfo_Impl for ActionWithProgress_Impl

{ + fn Id(&self) -> Result { + Ok(1) + } + fn Status(&self) -> Result { + Ok(self.0.status()) + } + fn ErrorCode(&self) -> Result { + Ok(self.0.error_code()) + } + fn Cancel(&self) -> Result<()> { + Ok(()) + } + fn Close(&self) -> Result<()> { + Ok(()) + } +} + +impl IAsyncInfo_Impl for OperationWithProgress_Impl { + fn Id(&self) -> Result { + Ok(1) + } + fn Status(&self) -> Result { + Ok(self.0.status()) + } + fn ErrorCode(&self) -> Result { + Ok(self.0.error_code()) + } + fn Cancel(&self) -> Result<()> { + Ok(()) + } + fn Close(&self) -> Result<()> { + Ok(()) + } +} + +impl IAsyncAction_Impl for Action_Impl { + fn SetCompleted(&self, handler: Ref) -> Result<()> { + self.0.set_completed(&self.as_interface(), handler) + } + fn Completed(&self) -> Result { + Err(Error::empty()) + } + fn GetResults(&self) -> Result<()> { + self.0.get_results() + } +} + +impl IAsyncOperation_Impl for Operation_Impl { + fn SetCompleted(&self, handler: Ref>) -> Result<()> { + self.0.set_completed(&self.as_interface(), handler) + } + fn Completed(&self) -> Result> { + Err(Error::empty()) + } + fn GetResults(&self) -> Result { + self.0.get_results() + } +} + +impl IAsyncActionWithProgress_Impl

for ActionWithProgress_Impl

{ + fn SetCompleted(&self, handler: Ref>) -> Result<()> { + self.0.set_completed(&self.as_interface(), handler) + } + fn Completed(&self) -> Result> { + Err(Error::empty()) + } + fn GetResults(&self) -> Result<()> { + self.0.get_results() + } + fn SetProgress(&self, _: Ref>) -> Result<()> { + Ok(()) + } + fn Progress(&self) -> Result> { + Err(Error::empty()) + } +} + +impl IAsyncOperationWithProgress_Impl + for OperationWithProgress_Impl +{ + fn SetCompleted( + &self, + handler: Ref>, + ) -> Result<()> { + self.0.set_completed(&self.as_interface(), handler) + } + fn Completed(&self) -> Result> { + Err(Error::empty()) + } + fn GetResults(&self) -> Result { + self.0.get_results() + } + fn SetProgress(&self, _: Ref>) -> Result<()> { + Ok(()) + } + fn Progress(&self) -> Result> { + Err(Error::empty()) + } +} + +impl IAsyncAction { + /// Creates an `IAsyncAction` that waits for the closure to execute on the Windows thread pool. + pub fn spawn(f: F) -> Self + where + F: FnOnce() -> Result<()> + Send + 'static, + { + let object = ComObject::new(Action(SyncState::new())); + let interface = object.to_interface(); + + windows_threading::submit(move || { + object.0.spawn(&object.as_interface(), f); + }); + + interface + } +} + +impl IAsyncOperation { + /// Creates an `IAsyncOperation` that waits for the closure to execute on the Windows thread pool. + pub fn spawn(f: F) -> Self + where + F: FnOnce() -> Result + Send + 'static, + { + let object = ComObject::new(Operation(SyncState::new())); + let interface = object.to_interface(); + + windows_threading::submit(move || { + object.0.spawn(&object.as_interface(), f); + }); + + interface + } +} + +impl IAsyncActionWithProgress

{ + /// Creates an `IAsyncActionWithProgress

` that waits for the closure to execute on the Windows thread pool. + pub fn spawn(f: F) -> Self + where + F: FnOnce() -> Result<()> + Send + 'static, + { + let object = ComObject::new(ActionWithProgress(SyncState::new())); + let interface = object.to_interface(); + + windows_threading::submit(move || { + object.0.spawn(&object.as_interface(), f); + }); + + interface + } +} + +impl IAsyncOperationWithProgress { + /// Creates an `IAsyncOperationWithProgress` that waits for the closure to execute on the Windows thread pool. + pub fn spawn(f: F) -> Self + where + F: FnOnce() -> Result + Send + 'static, + { + let object = ComObject::new(OperationWithProgress(SyncState::new())); + let interface = object.to_interface(); + + windows_threading::submit(move || { + object.0.spawn(&object.as_interface(), f); + }); + + interface + } +} diff --git a/third_party/windows-winui/windows-future/src/bindings.rs b/third_party/windows-winui/windows-future/src/bindings.rs new file mode 100644 index 0000000000..935da35575 --- /dev/null +++ b/third_party/windows-winui/windows-future/src/bindings.rs @@ -0,0 +1,2015 @@ +windows_core::imp::define_interface!( + AsyncActionCompletedHandler, + AsyncActionCompletedHandler_Vtbl, + 0xa4ed5c81_76c9_40bd_8be6_b1d90fb20ae7 +); +impl windows_core::RuntimeType for AsyncActionCompletedHandler { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +impl AsyncActionCompletedHandler { + pub fn new< + F: Fn(windows_core::Ref, AsyncStatus) -> windows_core::Result<()> + + Send + + 'static, + >( + invoke: F, + ) -> Self { + let com = windows_core::imp::DelegateBox::::new( + &AsyncActionCompletedHandlerBox::::VTABLE, + invoke, + ); + unsafe { core::mem::transmute(windows_core::imp::Box::new(com)) } + } + pub fn Invoke(&self, asyncinfo: P0, asyncstatus: AsyncStatus) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + unsafe { + (windows_core::Interface::vtable(self).Invoke)( + windows_core::Interface::as_raw(self), + asyncinfo.param().abi(), + asyncstatus, + ) + .ok() + } + } +} +#[repr(C)] +#[doc(hidden)] +pub struct AsyncActionCompletedHandler_Vtbl { + base__: windows_core::IUnknown_Vtbl, + Invoke: unsafe extern "system" fn( + this: *mut core::ffi::c_void, + asyncinfo: *mut core::ffi::c_void, + asyncstatus: AsyncStatus, + ) -> windows_core::HRESULT, +} +struct AsyncActionCompletedHandlerBox< + F: Fn(windows_core::Ref, AsyncStatus) -> windows_core::Result<()> + Send + 'static, +>(core::marker::PhantomData<(fn() -> F,)>); +impl< + F: Fn(windows_core::Ref, AsyncStatus) -> windows_core::Result<()> + + Send + + 'static, + > AsyncActionCompletedHandlerBox +{ + const VTABLE: AsyncActionCompletedHandler_Vtbl = AsyncActionCompletedHandler_Vtbl { + base__: windows_core::IUnknown_Vtbl { + QueryInterface: + windows_core::imp::DelegateBox::::QueryInterface, + AddRef: windows_core::imp::DelegateBox::::AddRef, + Release: windows_core::imp::DelegateBox::::Release, + }, + Invoke: Self::Invoke, + }; + unsafe extern "system" fn Invoke( + this: *mut core::ffi::c_void, + asyncinfo: *mut core::ffi::c_void, + asyncstatus: AsyncStatus, + ) -> windows_core::HRESULT { + unsafe { + let this = &mut *(this as *mut *mut core::ffi::c_void + as *mut windows_core::imp::DelegateBox); + (this.invoke)(core::mem::transmute_copy(&asyncinfo), asyncstatus).into() + } + } +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AsyncActionProgressHandler( + windows_core::IUnknown, + core::marker::PhantomData, +) +where + TProgress: windows_core::RuntimeType + 'static; +unsafe impl windows_core::Interface + for AsyncActionProgressHandler +{ + type Vtable = AsyncActionProgressHandler_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl windows_core::RuntimeType + for AsyncActionProgressHandler +{ + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({6d844858-0cff-4590-ae89-95a5a5c8b4b8}") + .push_slice(b";") + .push_other(TProgress::SIGNATURE) + .push_slice(b")"); +} +impl AsyncActionProgressHandler { + pub fn new< + F: Fn( + windows_core::Ref>, + windows_core::Ref, + ) -> windows_core::Result<()> + + Send + + 'static, + >( + invoke: F, + ) -> Self { + let com = windows_core::imp::DelegateBox::, F>::new( + &AsyncActionProgressHandlerBox::::VTABLE, + invoke, + ); + unsafe { core::mem::transmute(windows_core::imp::Box::new(com)) } + } + pub fn Invoke(&self, asyncinfo: P0, progressinfo: P1) -> windows_core::Result<()> + where + P0: windows_core::Param>, + P1: windows_core::Param, + { + unsafe { + (windows_core::Interface::vtable(self).Invoke)( + windows_core::Interface::as_raw(self), + asyncinfo.param().abi(), + progressinfo.param().abi(), + ) + .ok() + } + } +} +#[repr(C)] +#[doc(hidden)] +pub struct AsyncActionProgressHandler_Vtbl +where + TProgress: windows_core::RuntimeType + 'static, +{ + base__: windows_core::IUnknown_Vtbl, + Invoke: unsafe extern "system" fn( + this: *mut core::ffi::c_void, + asyncinfo: *mut core::ffi::c_void, + progressinfo: windows_core::AbiType, + ) -> windows_core::HRESULT, + TProgress: core::marker::PhantomData, +} +struct AsyncActionProgressHandlerBox< + TProgress, + F: Fn( + windows_core::Ref>, + windows_core::Ref, + ) -> windows_core::Result<()> + + Send + + 'static, +>(core::marker::PhantomData<(TProgress, fn() -> F)>) +where + TProgress: windows_core::RuntimeType + 'static; +impl< + TProgress: windows_core::RuntimeType + 'static, + F: Fn( + windows_core::Ref>, + windows_core::Ref, + ) -> windows_core::Result<()> + + Send + + 'static, + > AsyncActionProgressHandlerBox +{ + const VTABLE: AsyncActionProgressHandler_Vtbl = AsyncActionProgressHandler_Vtbl::< + TProgress, + > { + base__: windows_core::IUnknown_Vtbl { + QueryInterface: windows_core::imp::DelegateBox::< + AsyncActionProgressHandler, + F, + >::QueryInterface, + AddRef: + windows_core::imp::DelegateBox::, F>::AddRef, + Release: + windows_core::imp::DelegateBox::, F>::Release, + }, + Invoke: Self::Invoke, + TProgress: core::marker::PhantomData::, + }; + unsafe extern "system" fn Invoke( + this: *mut core::ffi::c_void, + asyncinfo: *mut core::ffi::c_void, + progressinfo: windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this = &mut *(this as *mut *mut core::ffi::c_void + as *mut windows_core::imp::DelegateBox, F>); + (this.invoke)( + core::mem::transmute_copy(&asyncinfo), + core::mem::transmute_copy(&progressinfo), + ) + .into() + } + } +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AsyncActionWithProgressCompletedHandler( + windows_core::IUnknown, + core::marker::PhantomData, +) +where + TProgress: windows_core::RuntimeType + 'static; +unsafe impl windows_core::Interface + for AsyncActionWithProgressCompletedHandler +{ + type Vtable = AsyncActionWithProgressCompletedHandler_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl windows_core::RuntimeType + for AsyncActionWithProgressCompletedHandler +{ + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({9c029f91-cc84-44fd-ac26-0a6c4e555281}") + .push_slice(b";") + .push_other(TProgress::SIGNATURE) + .push_slice(b")"); +} +impl + AsyncActionWithProgressCompletedHandler +{ + pub fn new< + F: Fn( + windows_core::Ref>, + AsyncStatus, + ) -> windows_core::Result<()> + + Send + + 'static, + >( + invoke: F, + ) -> Self { + let com = windows_core::imp::DelegateBox::< + AsyncActionWithProgressCompletedHandler, + F, + >::new( + &AsyncActionWithProgressCompletedHandlerBox::::VTABLE, + invoke, + ); + unsafe { core::mem::transmute(windows_core::imp::Box::new(com)) } + } + pub fn Invoke(&self, asyncinfo: P0, asyncstatus: AsyncStatus) -> windows_core::Result<()> + where + P0: windows_core::Param>, + { + unsafe { + (windows_core::Interface::vtable(self).Invoke)( + windows_core::Interface::as_raw(self), + asyncinfo.param().abi(), + asyncstatus, + ) + .ok() + } + } +} +#[repr(C)] +#[doc(hidden)] +pub struct AsyncActionWithProgressCompletedHandler_Vtbl +where + TProgress: windows_core::RuntimeType + 'static, +{ + base__: windows_core::IUnknown_Vtbl, + Invoke: unsafe extern "system" fn( + this: *mut core::ffi::c_void, + asyncinfo: *mut core::ffi::c_void, + asyncstatus: AsyncStatus, + ) -> windows_core::HRESULT, + TProgress: core::marker::PhantomData, +} +struct AsyncActionWithProgressCompletedHandlerBox< + TProgress, + F: Fn( + windows_core::Ref>, + AsyncStatus, + ) -> windows_core::Result<()> + + Send + + 'static, +>(core::marker::PhantomData<(TProgress, fn() -> F)>) +where + TProgress: windows_core::RuntimeType + 'static; +impl< + TProgress: windows_core::RuntimeType + 'static, + F: Fn( + windows_core::Ref>, + AsyncStatus, + ) -> windows_core::Result<()> + + Send + + 'static, + > AsyncActionWithProgressCompletedHandlerBox +{ + const VTABLE: AsyncActionWithProgressCompletedHandler_Vtbl = + AsyncActionWithProgressCompletedHandler_Vtbl:: { + base__: windows_core::IUnknown_Vtbl { + QueryInterface: windows_core::imp::DelegateBox::< + AsyncActionWithProgressCompletedHandler, + F, + >::QueryInterface, + AddRef: windows_core::imp::DelegateBox::< + AsyncActionWithProgressCompletedHandler, + F, + >::AddRef, + Release: windows_core::imp::DelegateBox::< + AsyncActionWithProgressCompletedHandler, + F, + >::Release, + }, + Invoke: Self::Invoke, + TProgress: core::marker::PhantomData::, + }; + unsafe extern "system" fn Invoke( + this: *mut core::ffi::c_void, + asyncinfo: *mut core::ffi::c_void, + asyncstatus: AsyncStatus, + ) -> windows_core::HRESULT { + unsafe { + let this = &mut *(this as *mut *mut core::ffi::c_void + as *mut windows_core::imp::DelegateBox< + AsyncActionWithProgressCompletedHandler, + F, + >); + (this.invoke)(core::mem::transmute_copy(&asyncinfo), asyncstatus).into() + } + } +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AsyncOperationCompletedHandler( + windows_core::IUnknown, + core::marker::PhantomData, +) +where + TResult: windows_core::RuntimeType + 'static; +unsafe impl windows_core::Interface + for AsyncOperationCompletedHandler +{ + type Vtable = AsyncOperationCompletedHandler_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl windows_core::RuntimeType + for AsyncOperationCompletedHandler +{ + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({fcdcf02c-e5d8-4478-915a-4d90b74b83a5}") + .push_slice(b";") + .push_other(TResult::SIGNATURE) + .push_slice(b")"); +} +impl AsyncOperationCompletedHandler { + pub fn new< + F: Fn(windows_core::Ref>, AsyncStatus) -> windows_core::Result<()> + + Send + + 'static, + >( + invoke: F, + ) -> Self { + let com = windows_core::imp::DelegateBox::, F>::new( + &AsyncOperationCompletedHandlerBox::::VTABLE, + invoke, + ); + unsafe { core::mem::transmute(windows_core::imp::Box::new(com)) } + } + pub fn Invoke(&self, asyncinfo: P0, asyncstatus: AsyncStatus) -> windows_core::Result<()> + where + P0: windows_core::Param>, + { + unsafe { + (windows_core::Interface::vtable(self).Invoke)( + windows_core::Interface::as_raw(self), + asyncinfo.param().abi(), + asyncstatus, + ) + .ok() + } + } +} +#[repr(C)] +#[doc(hidden)] +pub struct AsyncOperationCompletedHandler_Vtbl +where + TResult: windows_core::RuntimeType + 'static, +{ + base__: windows_core::IUnknown_Vtbl, + Invoke: unsafe extern "system" fn( + this: *mut core::ffi::c_void, + asyncinfo: *mut core::ffi::c_void, + asyncstatus: AsyncStatus, + ) -> windows_core::HRESULT, + TResult: core::marker::PhantomData, +} +struct AsyncOperationCompletedHandlerBox< + TResult, + F: Fn(windows_core::Ref>, AsyncStatus) -> windows_core::Result<()> + + Send + + 'static, +>(core::marker::PhantomData<(TResult, fn() -> F)>) +where + TResult: windows_core::RuntimeType + 'static; +impl< + TResult: windows_core::RuntimeType + 'static, + F: Fn(windows_core::Ref>, AsyncStatus) -> windows_core::Result<()> + + Send + + 'static, + > AsyncOperationCompletedHandlerBox +{ + const VTABLE: AsyncOperationCompletedHandler_Vtbl = + AsyncOperationCompletedHandler_Vtbl:: { + base__: + windows_core::IUnknown_Vtbl { + QueryInterface: windows_core::imp::DelegateBox::< + AsyncOperationCompletedHandler, + F, + >::QueryInterface, + AddRef: windows_core::imp::DelegateBox::< + AsyncOperationCompletedHandler, + F, + >::AddRef, + Release: windows_core::imp::DelegateBox::< + AsyncOperationCompletedHandler, + F, + >::Release, + }, + Invoke: Self::Invoke, + TResult: core::marker::PhantomData::, + }; + unsafe extern "system" fn Invoke( + this: *mut core::ffi::c_void, + asyncinfo: *mut core::ffi::c_void, + asyncstatus: AsyncStatus, + ) -> windows_core::HRESULT { + unsafe { + let this = &mut *(this as *mut *mut core::ffi::c_void + as *mut windows_core::imp::DelegateBox, F>); + (this.invoke)(core::mem::transmute_copy(&asyncinfo), asyncstatus).into() + } + } +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AsyncOperationProgressHandler( + windows_core::IUnknown, + core::marker::PhantomData, + core::marker::PhantomData, +) +where + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static; +unsafe impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > windows_core::Interface for AsyncOperationProgressHandler +{ + type Vtable = AsyncOperationProgressHandler_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > windows_core::RuntimeType for AsyncOperationProgressHandler +{ + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({55690902-0aab-421a-8778-f8ce5026d758}") + .push_slice(b";") + .push_other(TResult::SIGNATURE) + .push_slice(b";") + .push_other(TProgress::SIGNATURE) + .push_slice(b")"); +} +impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > AsyncOperationProgressHandler +{ + pub fn new< + F: Fn( + windows_core::Ref>, + windows_core::Ref, + ) -> windows_core::Result<()> + + Send + + 'static, + >( + invoke: F, + ) -> Self { + let com = windows_core::imp::DelegateBox::< + AsyncOperationProgressHandler, + F, + >::new( + &AsyncOperationProgressHandlerBox::::VTABLE, + invoke, + ); + unsafe { core::mem::transmute(windows_core::imp::Box::new(com)) } + } + pub fn Invoke(&self, asyncinfo: P0, progressinfo: P1) -> windows_core::Result<()> + where + P0: windows_core::Param>, + P1: windows_core::Param, + { + unsafe { + (windows_core::Interface::vtable(self).Invoke)( + windows_core::Interface::as_raw(self), + asyncinfo.param().abi(), + progressinfo.param().abi(), + ) + .ok() + } + } +} +#[repr(C)] +#[doc(hidden)] +pub struct AsyncOperationProgressHandler_Vtbl +where + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, +{ + base__: windows_core::IUnknown_Vtbl, + Invoke: unsafe extern "system" fn( + this: *mut core::ffi::c_void, + asyncinfo: *mut core::ffi::c_void, + progressinfo: windows_core::AbiType, + ) -> windows_core::HRESULT, + TResult: core::marker::PhantomData, + TProgress: core::marker::PhantomData, +} +struct AsyncOperationProgressHandlerBox< + TResult, + TProgress, + F: Fn( + windows_core::Ref>, + windows_core::Ref, + ) -> windows_core::Result<()> + + Send + + 'static, +>(core::marker::PhantomData<(TResult, TProgress, fn() -> F)>) +where + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static; +impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + F: Fn( + windows_core::Ref>, + windows_core::Ref, + ) -> windows_core::Result<()> + + Send + + 'static, + > AsyncOperationProgressHandlerBox +{ + const VTABLE: AsyncOperationProgressHandler_Vtbl = + AsyncOperationProgressHandler_Vtbl:: { + base__: windows_core::IUnknown_Vtbl { + QueryInterface: windows_core::imp::DelegateBox::< + AsyncOperationProgressHandler, + F, + >::QueryInterface, + AddRef: windows_core::imp::DelegateBox::< + AsyncOperationProgressHandler, + F, + >::AddRef, + Release: windows_core::imp::DelegateBox::< + AsyncOperationProgressHandler, + F, + >::Release, + }, + Invoke: Self::Invoke, + TResult: core::marker::PhantomData::, + TProgress: core::marker::PhantomData::, + }; + unsafe extern "system" fn Invoke( + this: *mut core::ffi::c_void, + asyncinfo: *mut core::ffi::c_void, + progressinfo: windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this = &mut *(this as *mut *mut core::ffi::c_void + as *mut windows_core::imp::DelegateBox< + AsyncOperationProgressHandler, + F, + >); + (this.invoke)( + core::mem::transmute_copy(&asyncinfo), + core::mem::transmute_copy(&progressinfo), + ) + .into() + } + } +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AsyncOperationWithProgressCompletedHandler( + windows_core::IUnknown, + core::marker::PhantomData, + core::marker::PhantomData, +) +where + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static; +unsafe impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > windows_core::Interface for AsyncOperationWithProgressCompletedHandler +{ + type Vtable = AsyncOperationWithProgressCompletedHandler_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > windows_core::RuntimeType for AsyncOperationWithProgressCompletedHandler +{ + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({e85df41d-6aa7-46e3-a8e2-f009d840c627}") + .push_slice(b";") + .push_other(TResult::SIGNATURE) + .push_slice(b";") + .push_other(TProgress::SIGNATURE) + .push_slice(b")"); +} +impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > AsyncOperationWithProgressCompletedHandler +{ + pub fn new< + F: Fn( + windows_core::Ref>, + AsyncStatus, + ) -> windows_core::Result<()> + + Send + + 'static, + >( + invoke: F, + ) -> Self { + let com = windows_core::imp::DelegateBox::< + AsyncOperationWithProgressCompletedHandler, + F, + >::new( + &AsyncOperationWithProgressCompletedHandlerBox::::VTABLE, + invoke, + ); + unsafe { core::mem::transmute(windows_core::imp::Box::new(com)) } + } + pub fn Invoke(&self, asyncinfo: P0, asyncstatus: AsyncStatus) -> windows_core::Result<()> + where + P0: windows_core::Param>, + { + unsafe { + (windows_core::Interface::vtable(self).Invoke)( + windows_core::Interface::as_raw(self), + asyncinfo.param().abi(), + asyncstatus, + ) + .ok() + } + } +} +#[repr(C)] +#[doc(hidden)] +pub struct AsyncOperationWithProgressCompletedHandler_Vtbl +where + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, +{ + base__: windows_core::IUnknown_Vtbl, + Invoke: unsafe extern "system" fn( + this: *mut core::ffi::c_void, + asyncinfo: *mut core::ffi::c_void, + asyncstatus: AsyncStatus, + ) -> windows_core::HRESULT, + TResult: core::marker::PhantomData, + TProgress: core::marker::PhantomData, +} +struct AsyncOperationWithProgressCompletedHandlerBox< + TResult, + TProgress, + F: Fn( + windows_core::Ref>, + AsyncStatus, + ) -> windows_core::Result<()> + + Send + + 'static, +>(core::marker::PhantomData<(TResult, TProgress, fn() -> F)>) +where + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static; +impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + F: Fn( + windows_core::Ref>, + AsyncStatus, + ) -> windows_core::Result<()> + + Send + + 'static, + > AsyncOperationWithProgressCompletedHandlerBox +{ + const VTABLE: AsyncOperationWithProgressCompletedHandler_Vtbl = + AsyncOperationWithProgressCompletedHandler_Vtbl:: { + base__: windows_core::IUnknown_Vtbl { + QueryInterface: windows_core::imp::DelegateBox::< + AsyncOperationWithProgressCompletedHandler, + F, + >::QueryInterface, + AddRef: windows_core::imp::DelegateBox::< + AsyncOperationWithProgressCompletedHandler, + F, + >::AddRef, + Release: windows_core::imp::DelegateBox::< + AsyncOperationWithProgressCompletedHandler, + F, + >::Release, + }, + Invoke: Self::Invoke, + TResult: core::marker::PhantomData::, + TProgress: core::marker::PhantomData::, + }; + unsafe extern "system" fn Invoke( + this: *mut core::ffi::c_void, + asyncinfo: *mut core::ffi::c_void, + asyncstatus: AsyncStatus, + ) -> windows_core::HRESULT { + unsafe { + let this = &mut *(this as *mut *mut core::ffi::c_void + as *mut windows_core::imp::DelegateBox< + AsyncOperationWithProgressCompletedHandler, + F, + >); + (this.invoke)(core::mem::transmute_copy(&asyncinfo), asyncstatus).into() + } + } +} +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct AsyncStatus(pub i32); +impl AsyncStatus { + pub const Canceled: Self = Self(2i32); + pub const Completed: Self = Self(1i32); + pub const Error: Self = Self(3i32); + pub const Started: Self = Self(0i32); +} +impl windows_core::TypeKind for AsyncStatus { + type TypeKind = windows_core::CopyType; +} +impl windows_core::RuntimeType for AsyncStatus { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::from_slice(b"enum(Windows.Foundation.AsyncStatus;i4)"); + const NAME: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::from_slice(b"Windows.Foundation.AsyncStatus"); +} +windows_core::imp::define_interface!( + IAsyncAction, + IAsyncAction_Vtbl, + 0x5a648006_843a_4da9_865b_9d26e5dfad7b +); +impl windows_core::RuntimeType for IAsyncAction { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); + const NAME: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::from_slice(b"Windows.Foundation.IAsyncAction"); +} +windows_core::imp::interface_hierarchy!( + IAsyncAction, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!(IAsyncAction, IAsyncInfo); +impl IAsyncAction { + pub fn SetCompleted(&self, handler: P0) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + unsafe { + (windows_core::Interface::vtable(self).SetCompleted)( + windows_core::Interface::as_raw(self), + handler.param().abi(), + ) + .ok() + } + } + pub fn Completed(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Completed)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn GetResults(&self) -> windows_core::Result<()> { + unsafe { + (windows_core::Interface::vtable(self).GetResults)(windows_core::Interface::as_raw( + self, + )) + .ok() + } + } + pub fn Id(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Id)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Status(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Status)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn ErrorCode(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).ErrorCode)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Cancel(&self) -> windows_core::Result<()> { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + (windows_core::Interface::vtable(this).Cancel)(windows_core::Interface::as_raw(this)) + .ok() + } + } + pub fn Close(&self) -> windows_core::Result<()> { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + (windows_core::Interface::vtable(this).Close)(windows_core::Interface::as_raw(this)) + .ok() + } + } +} +unsafe impl Send for IAsyncAction {} +unsafe impl Sync for IAsyncAction {} +impl windows_core::RuntimeName for IAsyncAction { + const NAME: &'static str = "Windows.Foundation.IAsyncAction"; +} +pub trait IAsyncAction_Impl: IAsyncInfo_Impl { + fn SetCompleted( + &self, + handler: windows_core::Ref, + ) -> windows_core::Result<()>; + fn Completed(&self) -> windows_core::Result; + fn GetResults(&self) -> windows_core::Result<()>; +} +impl IAsyncAction_Vtbl { + pub const fn new() -> Self { + unsafe extern "system" fn SetCompleted( + this: *mut core::ffi::c_void, + handler: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IAsyncAction_Impl::SetCompleted(this, core::mem::transmute_copy(&handler)).into() + } + } + unsafe extern "system" fn Completed( + this: *mut core::ffi::c_void, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IAsyncAction_Impl::Completed(this) { + 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 GetResults( + this: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IAsyncAction_Impl::GetResults(this).into() + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::(), + SetCompleted: SetCompleted::, + Completed: Completed::, + GetResults: GetResults::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IAsyncAction_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub SetCompleted: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub Completed: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub GetResults: unsafe extern "system" fn(*mut core::ffi::c_void) -> windows_core::HRESULT, +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IAsyncActionWithProgress( + windows_core::IUnknown, + core::marker::PhantomData, +) +where + TProgress: windows_core::RuntimeType + 'static; +impl + windows_core::imp::CanInto for IAsyncActionWithProgress +{ +} +impl + windows_core::imp::CanInto for IAsyncActionWithProgress +{ +} +unsafe impl windows_core::Interface + for IAsyncActionWithProgress +{ + type Vtable = IAsyncActionWithProgress_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl windows_core::RuntimeType + for IAsyncActionWithProgress +{ + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({1f6db258-e803-48a1-9546-eb7353398884}") + .push_slice(b";") + .push_other(TProgress::SIGNATURE) + .push_slice(b")"); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"Windows.Foundation.IAsyncActionWithProgress`1<") + .push_other(TProgress::NAME) + .push_slice(b">"); +} +impl windows_core::imp::CanInto + for IAsyncActionWithProgress +{ + const QUERY: bool = true; +} +impl IAsyncActionWithProgress { + pub fn SetProgress(&self, handler: P0) -> windows_core::Result<()> + where + P0: windows_core::Param>, + { + unsafe { + (windows_core::Interface::vtable(self).SetProgress)( + windows_core::Interface::as_raw(self), + handler.param().abi(), + ) + .ok() + } + } + pub fn Progress(&self) -> windows_core::Result> { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Progress)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn SetCompleted(&self, handler: P0) -> windows_core::Result<()> + where + P0: windows_core::Param>, + { + unsafe { + (windows_core::Interface::vtable(self).SetCompleted)( + windows_core::Interface::as_raw(self), + handler.param().abi(), + ) + .ok() + } + } + pub fn Completed( + &self, + ) -> windows_core::Result> { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Completed)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn GetResults(&self) -> windows_core::Result<()> { + unsafe { + (windows_core::Interface::vtable(self).GetResults)(windows_core::Interface::as_raw( + self, + )) + .ok() + } + } + pub fn Id(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Id)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Status(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Status)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn ErrorCode(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).ErrorCode)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Cancel(&self) -> windows_core::Result<()> { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + (windows_core::Interface::vtable(this).Cancel)(windows_core::Interface::as_raw(this)) + .ok() + } + } + pub fn Close(&self) -> windows_core::Result<()> { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + (windows_core::Interface::vtable(this).Close)(windows_core::Interface::as_raw(this)) + .ok() + } + } +} +unsafe impl Send + for IAsyncActionWithProgress +{ +} +unsafe impl Sync + for IAsyncActionWithProgress +{ +} +impl windows_core::RuntimeName + for IAsyncActionWithProgress +{ + const NAME: &'static str = "Windows.Foundation.IAsyncActionWithProgress"; + const RUNTIME_CLASS_NAME: windows_core::imp::ConstBuffer = + ::NAME; +} +pub trait IAsyncActionWithProgress_Impl: IAsyncInfo_Impl +where + TProgress: windows_core::RuntimeType + 'static, +{ + fn SetProgress( + &self, + handler: windows_core::Ref>, + ) -> windows_core::Result<()>; + fn Progress(&self) -> windows_core::Result>; + fn SetCompleted( + &self, + handler: windows_core::Ref>, + ) -> windows_core::Result<()>; + fn Completed(&self) + -> windows_core::Result>; + fn GetResults(&self) -> windows_core::Result<()>; +} +impl IAsyncActionWithProgress_Vtbl { + pub const fn new, const OFFSET: isize>( + ) -> Self { + unsafe extern "system" fn SetProgress< + TProgress: windows_core::RuntimeType + 'static, + Identity: IAsyncActionWithProgress_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + handler: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IAsyncActionWithProgress_Impl::SetProgress( + this, + core::mem::transmute_copy(&handler), + ) + .into() + } + } + unsafe extern "system" fn Progress< + TProgress: windows_core::RuntimeType + 'static, + Identity: IAsyncActionWithProgress_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IAsyncActionWithProgress_Impl::Progress(this) { + 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 SetCompleted< + TProgress: windows_core::RuntimeType + 'static, + Identity: IAsyncActionWithProgress_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + handler: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IAsyncActionWithProgress_Impl::SetCompleted( + this, + core::mem::transmute_copy(&handler), + ) + .into() + } + } + unsafe extern "system" fn Completed< + TProgress: windows_core::RuntimeType + 'static, + Identity: IAsyncActionWithProgress_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IAsyncActionWithProgress_Impl::Completed(this) { + 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 GetResults< + TProgress: windows_core::RuntimeType + 'static, + Identity: IAsyncActionWithProgress_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IAsyncActionWithProgress_Impl::GetResults(this).into() + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::< + Identity, + IAsyncActionWithProgress, + OFFSET, + >(), + SetProgress: SetProgress::, + Progress: Progress::, + SetCompleted: SetCompleted::, + Completed: Completed::, + GetResults: GetResults::, + TProgress: core::marker::PhantomData::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == & as windows_core::Interface>::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IAsyncActionWithProgress_Vtbl +where + TProgress: windows_core::RuntimeType + 'static, +{ + pub base__: windows_core::IInspectable_Vtbl, + pub SetProgress: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub Progress: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub SetCompleted: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub Completed: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub GetResults: unsafe extern "system" fn(*mut core::ffi::c_void) -> windows_core::HRESULT, + TProgress: core::marker::PhantomData, +} +windows_core::imp::define_interface!( + IAsyncInfo, + IAsyncInfo_Vtbl, + 0x00000036_0000_0000_c000_000000000046 +); +impl windows_core::RuntimeType for IAsyncInfo { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); + const NAME: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::from_slice(b"Windows.Foundation.IAsyncInfo"); +} +windows_core::imp::interface_hierarchy!( + IAsyncInfo, + windows_core::IUnknown, + windows_core::IInspectable +); +impl IAsyncInfo { + pub fn Id(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Id)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Status(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Status)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .map(|| result__) + } + } + pub fn ErrorCode(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).ErrorCode)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Cancel(&self) -> windows_core::Result<()> { + unsafe { + (windows_core::Interface::vtable(self).Cancel)(windows_core::Interface::as_raw(self)) + .ok() + } + } + pub fn Close(&self) -> windows_core::Result<()> { + unsafe { + (windows_core::Interface::vtable(self).Close)(windows_core::Interface::as_raw(self)) + .ok() + } + } +} +impl windows_core::RuntimeName for IAsyncInfo { + const NAME: &'static str = "Windows.Foundation.IAsyncInfo"; +} +pub trait IAsyncInfo_Impl: windows_core::IUnknownImpl { + fn Id(&self) -> windows_core::Result; + fn Status(&self) -> windows_core::Result; + fn ErrorCode(&self) -> windows_core::Result; + fn Cancel(&self) -> windows_core::Result<()>; + fn Close(&self) -> windows_core::Result<()>; +} +impl IAsyncInfo_Vtbl { + pub const fn new() -> Self { + unsafe extern "system" fn Id( + this: *mut core::ffi::c_void, + result__: *mut u32, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IAsyncInfo_Impl::Id(this) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn Status( + this: *mut core::ffi::c_void, + result__: *mut AsyncStatus, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IAsyncInfo_Impl::Status(this) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn ErrorCode( + this: *mut core::ffi::c_void, + result__: *mut windows_core::HRESULT, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IAsyncInfo_Impl::ErrorCode(this) { + Ok(ok__) => { + result__.write(ok__); + windows_core::HRESULT(0) + } + Err(err) => err.into(), + } + } + } + unsafe extern "system" fn Cancel( + this: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IAsyncInfo_Impl::Cancel(this).into() + } + } + unsafe extern "system" fn Close( + this: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IAsyncInfo_Impl::Close(this).into() + } + } + Self { + base__: windows_core::IInspectable_Vtbl::new::(), + Id: Id::, + Status: Status::, + ErrorCode: ErrorCode::, + Cancel: Cancel::, + Close: Close::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == &::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IAsyncInfo_Vtbl { + pub base__: windows_core::IInspectable_Vtbl, + pub Id: unsafe extern "system" fn(*mut core::ffi::c_void, *mut u32) -> windows_core::HRESULT, + pub Status: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut AsyncStatus, + ) -> windows_core::HRESULT, + pub ErrorCode: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut windows_core::HRESULT, + ) -> windows_core::HRESULT, + pub Cancel: unsafe extern "system" fn(*mut core::ffi::c_void) -> windows_core::HRESULT, + pub Close: unsafe extern "system" fn(*mut core::ffi::c_void) -> windows_core::HRESULT, +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IAsyncOperation(windows_core::IUnknown, core::marker::PhantomData) +where + TResult: windows_core::RuntimeType + 'static; +impl + windows_core::imp::CanInto for IAsyncOperation +{ +} +impl + windows_core::imp::CanInto for IAsyncOperation +{ +} +unsafe impl windows_core::Interface + for IAsyncOperation +{ + type Vtable = IAsyncOperation_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl windows_core::RuntimeType + for IAsyncOperation +{ + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({9fc2b0bb-e446-44e2-aa61-9cab8f636af2}") + .push_slice(b";") + .push_other(TResult::SIGNATURE) + .push_slice(b")"); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"Windows.Foundation.IAsyncOperation`1<") + .push_other(TResult::NAME) + .push_slice(b">"); +} +impl windows_core::imp::CanInto + for IAsyncOperation +{ + const QUERY: bool = true; +} +impl IAsyncOperation { + pub fn SetCompleted(&self, handler: P0) -> windows_core::Result<()> + where + P0: windows_core::Param>, + { + unsafe { + (windows_core::Interface::vtable(self).SetCompleted)( + windows_core::Interface::as_raw(self), + handler.param().abi(), + ) + .ok() + } + } + pub fn Completed(&self) -> windows_core::Result> { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Completed)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn GetResults(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).GetResults)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn Id(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Id)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Status(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Status)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn ErrorCode(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).ErrorCode)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Cancel(&self) -> windows_core::Result<()> { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + (windows_core::Interface::vtable(this).Cancel)(windows_core::Interface::as_raw(this)) + .ok() + } + } + pub fn Close(&self) -> windows_core::Result<()> { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + (windows_core::Interface::vtable(this).Close)(windows_core::Interface::as_raw(this)) + .ok() + } + } +} +unsafe impl Send for IAsyncOperation {} +unsafe impl Sync for IAsyncOperation {} +impl windows_core::RuntimeName + for IAsyncOperation +{ + const NAME: &'static str = "Windows.Foundation.IAsyncOperation"; + const RUNTIME_CLASS_NAME: windows_core::imp::ConstBuffer = + ::NAME; +} +pub trait IAsyncOperation_Impl: IAsyncInfo_Impl +where + TResult: windows_core::RuntimeType + 'static, +{ + fn SetCompleted( + &self, + handler: windows_core::Ref>, + ) -> windows_core::Result<()>; + fn Completed(&self) -> windows_core::Result>; + fn GetResults(&self) -> windows_core::Result; +} +impl IAsyncOperation_Vtbl { + pub const fn new, const OFFSET: isize>() -> Self { + unsafe extern "system" fn SetCompleted< + TResult: windows_core::RuntimeType + 'static, + Identity: IAsyncOperation_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + handler: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IAsyncOperation_Impl::SetCompleted(this, core::mem::transmute_copy(&handler)).into() + } + } + unsafe extern "system" fn Completed< + TResult: windows_core::RuntimeType + 'static, + Identity: IAsyncOperation_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IAsyncOperation_Impl::Completed(this) { + 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 GetResults< + TResult: windows_core::RuntimeType + 'static, + Identity: IAsyncOperation_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IAsyncOperation_Impl::GetResults(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::< + Identity, + IAsyncOperation, + OFFSET, + >(), + SetCompleted: SetCompleted::, + Completed: Completed::, + GetResults: GetResults::, + TResult: core::marker::PhantomData::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == & as windows_core::Interface>::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IAsyncOperation_Vtbl +where + TResult: windows_core::RuntimeType + 'static, +{ + pub base__: windows_core::IInspectable_Vtbl, + pub SetCompleted: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub Completed: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub GetResults: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut windows_core::AbiType, + ) -> windows_core::HRESULT, + TResult: core::marker::PhantomData, +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IAsyncOperationWithProgress( + windows_core::IUnknown, + core::marker::PhantomData, + core::marker::PhantomData, +) +where + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static; +impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > windows_core::imp::CanInto + for IAsyncOperationWithProgress +{ +} +impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > windows_core::imp::CanInto + for IAsyncOperationWithProgress +{ +} +unsafe impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > windows_core::Interface for IAsyncOperationWithProgress +{ + type Vtable = IAsyncOperationWithProgress_Vtbl; + const IID: windows_core::GUID = + windows_core::GUID::from_signature(::SIGNATURE); +} +impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > windows_core::RuntimeType for IAsyncOperationWithProgress +{ + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"pinterface({b5d036d7-e297-498f-ba60-0289e76e23dd}") + .push_slice(b";") + .push_other(TResult::SIGNATURE) + .push_slice(b";") + .push_other(TProgress::SIGNATURE) + .push_slice(b")"); + const NAME: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::new() + .push_slice(b"Windows.Foundation.IAsyncOperationWithProgress`2<") + .push_other(TResult::NAME) + .push_slice(b", ") + .push_other(TProgress::NAME) + .push_slice(b">"); +} +impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > windows_core::imp::CanInto for IAsyncOperationWithProgress +{ + const QUERY: bool = true; +} +impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > IAsyncOperationWithProgress +{ + pub fn SetProgress(&self, handler: P0) -> windows_core::Result<()> + where + P0: windows_core::Param>, + { + unsafe { + (windows_core::Interface::vtable(self).SetProgress)( + windows_core::Interface::as_raw(self), + handler.param().abi(), + ) + .ok() + } + } + pub fn Progress( + &self, + ) -> windows_core::Result> { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Progress)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn SetCompleted(&self, handler: P0) -> windows_core::Result<()> + where + P0: windows_core::Param>, + { + unsafe { + (windows_core::Interface::vtable(self).SetCompleted)( + windows_core::Interface::as_raw(self), + handler.param().abi(), + ) + .ok() + } + } + pub fn Completed( + &self, + ) -> windows_core::Result> { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).Completed)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn GetResults(&self) -> windows_core::Result { + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(self).GetResults)( + windows_core::Interface::as_raw(self), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + } + } + pub fn Id(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Id)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Status(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).Status)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn ErrorCode(&self) -> windows_core::Result { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).ErrorCode)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .map(|| result__) + } + } + pub fn Cancel(&self) -> windows_core::Result<()> { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + (windows_core::Interface::vtable(this).Cancel)(windows_core::Interface::as_raw(this)) + .ok() + } + } + pub fn Close(&self) -> windows_core::Result<()> { + let this = &windows_core::Interface::cast::(self)?; + unsafe { + (windows_core::Interface::vtable(this).Close)(windows_core::Interface::as_raw(this)) + .ok() + } + } +} +unsafe impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > Send for IAsyncOperationWithProgress +{ +} +unsafe impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > Sync for IAsyncOperationWithProgress +{ +} +impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > windows_core::RuntimeName for IAsyncOperationWithProgress +{ + const NAME: &'static str = "Windows.Foundation.IAsyncOperationWithProgress"; + const RUNTIME_CLASS_NAME: windows_core::imp::ConstBuffer = + ::NAME; +} +pub trait IAsyncOperationWithProgress_Impl: IAsyncInfo_Impl +where + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, +{ + fn SetProgress( + &self, + handler: windows_core::Ref>, + ) -> windows_core::Result<()>; + fn Progress(&self) -> windows_core::Result>; + fn SetCompleted( + &self, + handler: windows_core::Ref>, + ) -> windows_core::Result<()>; + fn Completed( + &self, + ) -> windows_core::Result>; + fn GetResults(&self) -> windows_core::Result; +} +impl< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + > IAsyncOperationWithProgress_Vtbl +{ + pub const fn new< + Identity: IAsyncOperationWithProgress_Impl, + const OFFSET: isize, + >() -> Self { + unsafe extern "system" fn SetProgress< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + Identity: IAsyncOperationWithProgress_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + handler: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IAsyncOperationWithProgress_Impl::SetProgress( + this, + core::mem::transmute_copy(&handler), + ) + .into() + } + } + unsafe extern "system" fn Progress< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + Identity: IAsyncOperationWithProgress_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IAsyncOperationWithProgress_Impl::Progress(this) { + 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 SetCompleted< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + Identity: IAsyncOperationWithProgress_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + handler: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + IAsyncOperationWithProgress_Impl::SetCompleted( + this, + core::mem::transmute_copy(&handler), + ) + .into() + } + } + unsafe extern "system" fn Completed< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + Identity: IAsyncOperationWithProgress_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IAsyncOperationWithProgress_Impl::Completed(this) { + 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 GetResults< + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, + Identity: IAsyncOperationWithProgress_Impl, + const OFFSET: isize, + >( + this: *mut core::ffi::c_void, + result__: *mut windows_core::AbiType, + ) -> windows_core::HRESULT { + unsafe { + let this: &Identity = + &*((this as *const *const ()).offset(OFFSET) as *const Identity); + match IAsyncOperationWithProgress_Impl::GetResults(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::< + Identity, + IAsyncOperationWithProgress, + OFFSET, + >(), + SetProgress: SetProgress::, + Progress: Progress::, + SetCompleted: SetCompleted::, + Completed: Completed::, + GetResults: GetResults::, + TResult: core::marker::PhantomData::, + TProgress: core::marker::PhantomData::, + } + } + pub fn matches(iid: &windows_core::GUID) -> bool { + iid == & as windows_core::Interface>::IID + } +} +#[repr(C)] +#[doc(hidden)] +pub struct IAsyncOperationWithProgress_Vtbl +where + TResult: windows_core::RuntimeType + 'static, + TProgress: windows_core::RuntimeType + 'static, +{ + pub base__: windows_core::IInspectable_Vtbl, + pub SetProgress: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub Progress: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub SetCompleted: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub Completed: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut *mut core::ffi::c_void, + ) -> windows_core::HRESULT, + pub GetResults: unsafe extern "system" fn( + *mut core::ffi::c_void, + *mut windows_core::AbiType, + ) -> windows_core::HRESULT, + TResult: core::marker::PhantomData, + TProgress: core::marker::PhantomData, +} diff --git a/third_party/windows-winui/windows-future/src/bindings_impl.rs b/third_party/windows-winui/windows-future/src/bindings_impl.rs new file mode 100644 index 0000000000..8156f0bd70 --- /dev/null +++ b/third_party/windows-winui/windows-future/src/bindings_impl.rs @@ -0,0 +1,20 @@ +windows_link::link!("kernel32.dll" "system" fn CloseHandle(hobject : HANDLE) -> BOOL); +windows_link::link!("kernel32.dll" "system" fn CreateEventW(lpeventattributes : *const SECURITY_ATTRIBUTES, bmanualreset : BOOL, binitialstate : BOOL, lpname : PCWSTR) -> HANDLE); +windows_link::link!("kernel32.dll" "system" fn SetEvent(hevent : HANDLE) -> BOOL); +windows_link::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT); +pub type BOOL = i32; +pub type HANDLE = *mut core::ffi::c_void; +pub type PCWSTR = *const u16; +#[repr(C)] +#[derive(Clone, Copy)] +pub struct SECURITY_ATTRIBUTES { + pub nLength: u32, + pub lpSecurityDescriptor: *mut core::ffi::c_void, + pub bInheritHandle: BOOL, +} +impl Default for SECURITY_ATTRIBUTES { + fn default() -> Self { + unsafe { core::mem::zeroed() } + } +} +pub type WAIT_EVENT = u32; diff --git a/third_party/windows-winui/windows-future/src/future.rs b/third_party/windows-winui/windows-future/src/future.rs new file mode 100644 index 0000000000..bd57857721 --- /dev/null +++ b/third_party/windows-winui/windows-future/src/future.rs @@ -0,0 +1,121 @@ +use super::*; +use std::future::{Future, IntoFuture}; +use std::pin::Pin; +use std::sync::{Arc, Mutex}; +use std::task::{Context, Poll, Waker}; + +// The `AsyncFuture` is needed to store some extra state needed to keep async execution up to date with possible changes +// to Rust execution context. Each async type implements `IntoFuture` rather than implementing `Future` directly so that +// this adapter may be used. +pub struct AsyncFuture { + // Represents the async execution and provides the virtual methods for setting up a `Completed` handler and + // calling `GetResults` when execution is completed. + inner: A, + + // Provides the `Status` virtual method and saves repeated calls to `QueryInterface` during polling. + status: IAsyncInfo, + + // A shared waker is needed to keep the `Completed` handler updated. + // - `Option` is used to avoid allocations for async objects that have already completed. + // - `Arc` is used to share the `Waker` with the `Completed` handler and potentially replace the `Waker` + // since we don't have the ability to replace the `Completed` handler itself. + // - `Mutex` is used to synchronize replacing the `Waker` across threads. + waker: Option>>, +} + +impl AsyncFuture { + fn new(inner: A) -> Self { + Self { + // All four async interfaces implement `IAsyncInfo` so this `cast` will always succeed. + status: inner.cast().unwrap(), + inner, + waker: None, + } + } +} + +unsafe impl Send for AsyncFuture {} +unsafe impl Sync for AsyncFuture {} +impl Unpin for AsyncFuture {} + +impl Future for AsyncFuture { + type Output = Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + // A status of `Started` just means async execution is still in flight. Since WinRT async is always + // "hot start", if its not `Started` then its ready for us to call `GetResults` so we can skip all of + // the remaining set up. + if self.status.Status()? != AsyncStatus::Started { + return Poll::Ready(self.inner.get_results()); + } + + if let Some(shared_waker) = &self.waker { + // We have a shared waker which means we're either getting polled again or been transferred to + // another execution context. As we can't tell the difference, we need to update the shared waker + // to make sure we've got the "current" waker. + let mut guard = shared_waker.lock().unwrap(); + guard.clone_from(cx.waker()); + + // It may be possible that the `Completed` handler acquired the lock and signaled the old waker + // before we managed to acquire the lock to update it with the current waker. We check the status + // again here just in case this happens. + if self.status.Status()? != AsyncStatus::Started { + return Poll::Ready(self.inner.get_results()); + } + } else { + // If we don't have a saved waker it means this is the first time we're getting polled and should + // create the shared waker and set up a `Completed` handler. + let shared_waker = Arc::new(Mutex::new(cx.waker().clone())); + self.waker = Some(shared_waker.clone()); + + // Note that the handler can only be set once, which is why we need a shared waker in the first + // place. On the other hand, the handler will get called even if async execution has already + // completed, so we can just return `Pending` after setting the Completed handler. + self.inner.set_completed(move |_| { + shared_waker.lock().unwrap().wake_by_ref(); + })?; + }; + + Poll::Pending + } +} + +// +// The four `IntoFuture` trait implementations. +// + +impl IntoFuture for IAsyncAction { + type Output = Result<()>; + type IntoFuture = AsyncFuture; + + fn into_future(self) -> Self::IntoFuture { + AsyncFuture::new(self) + } +} + +impl IntoFuture for IAsyncOperation { + type Output = Result; + type IntoFuture = AsyncFuture; + + fn into_future(self) -> Self::IntoFuture { + AsyncFuture::new(self) + } +} + +impl IntoFuture for IAsyncActionWithProgress

{ + type Output = Result<()>; + type IntoFuture = AsyncFuture; + + fn into_future(self) -> Self::IntoFuture { + AsyncFuture::new(self) + } +} + +impl IntoFuture for IAsyncOperationWithProgress { + type Output = Result; + type IntoFuture = AsyncFuture; + + fn into_future(self) -> Self::IntoFuture { + AsyncFuture::new(self) + } +} diff --git a/third_party/windows-winui/windows-future/src/join.rs b/third_party/windows-winui/windows-future/src/join.rs new file mode 100644 index 0000000000..3e29c62af9 --- /dev/null +++ b/third_party/windows-winui/windows-future/src/join.rs @@ -0,0 +1,29 @@ +use super::*; + +impl IAsyncAction { + /// Waits for the `IAsyncAction` to finish. + pub fn join(&self) -> Result<()> { + Async::join(self) + } +} + +impl IAsyncOperation { + /// Waits for the `IAsyncOperation` to finish. + pub fn join(&self) -> Result { + Async::join(self) + } +} + +impl IAsyncActionWithProgress

{ + /// Waits for the `IAsyncActionWithProgress

` to finish. + pub fn join(&self) -> Result<()> { + Async::join(self) + } +} + +impl IAsyncOperationWithProgress { + /// Waits for the `IAsyncOperationWithProgress` to finish. + pub fn join(&self) -> Result { + Async::join(self) + } +} diff --git a/third_party/windows-winui/windows-future/src/lib.rs b/third_party/windows-winui/windows-future/src/lib.rs new file mode 100644 index 0000000000..045ce2dc2a --- /dev/null +++ b/third_party/windows-winui/windows-future/src/lib.rs @@ -0,0 +1,31 @@ +#![expect( + missing_docs, + non_snake_case, + non_camel_case_types, + non_upper_case_globals, + clippy::all +)] +#![doc = include_str!("../readme.md")] +#![cfg_attr(all(not(feature = "std")), no_std)] + +mod r#async; +mod bindings; +#[cfg(windows)] +mod bindings_impl; +mod join; +mod waiter; +mod when; + +pub use bindings::*; +#[cfg(windows)] +use bindings_impl::*; +use r#async::*; +use waiter::*; +use windows_core::*; + +#[cfg(feature = "std")] +mod async_ready; +#[cfg(feature = "std")] +mod async_spawn; +#[cfg(feature = "std")] +mod future; diff --git a/third_party/windows-winui/windows-future/src/waiter.rs b/third_party/windows-winui/windows-future/src/waiter.rs new file mode 100644 index 0000000000..e55e70c774 --- /dev/null +++ b/third_party/windows-winui/windows-future/src/waiter.rs @@ -0,0 +1,89 @@ +#[cfg(windows)] +mod imp { + use super::super::*; + + pub struct Waiter(HANDLE); + pub struct WaiterSignaler(HANDLE); + unsafe impl Send for WaiterSignaler {} + + impl Waiter { + pub fn new() -> crate::Result<(Self, WaiterSignaler)> { + unsafe { + let handle = CreateEventW(core::ptr::null(), 1, 0, core::ptr::null()); + if handle.is_null() { + Err(crate::Error::from_thread()) + } else { + Ok((Self(handle), WaiterSignaler(handle))) + } + } + } + + // Waits for the `WaiterSignaler` to signal and then closes the handle. + pub fn wait(self) { + unsafe { + WaitForSingleObject(self.0, 0xFFFFFFFF); + } + } + } + + impl WaiterSignaler { + /// # Safety + /// Signals the `Waiter`. This is unsafe because the lifetime of `WaiterSignaler` is not tied + /// to the lifetime of the `Waiter`. This is not possible in this case because the `Waiter` + /// is used to signal a WinRT async completion and the compiler doesn't know that the lifetime + /// of the delegate is bounded by the calling function. + pub unsafe fn signal(&self) { + // https://github.com/microsoft/windows-rs/pull/374#discussion_r535313344 + unsafe { + SetEvent(self.0); + } + } + } + + impl Drop for Waiter { + fn drop(&mut self) { + unsafe { + CloseHandle(self.0); + } + } + } +} + +// Non-Windows fallback using std primitives. +#[cfg(all(not(windows), feature = "std"))] +mod imp { + pub struct Waiter(std::sync::Arc<(std::sync::Mutex, std::sync::Condvar)>); + + pub struct WaiterSignaler(std::sync::Arc<(std::sync::Mutex, std::sync::Condvar)>); + + impl Waiter { + pub fn new() -> crate::Result<(Self, WaiterSignaler)> { + let state = + std::sync::Arc::new((std::sync::Mutex::new(false), std::sync::Condvar::new())); + Ok((Self(state.clone()), WaiterSignaler(state))) + } + + pub fn wait(self) { + let (lock, cvar) = &*self.0; + let mut signaled = lock.lock().unwrap(); + while !*signaled { + signaled = cvar.wait(signaled).unwrap(); + } + } + } + + impl WaiterSignaler { + /// # Safety + /// Matches the Windows signature. The non-Windows implementation is itself safe because the + /// state is reference counted, but the function is kept `unsafe` for API parity. + pub unsafe fn signal(&self) { + let (lock, cvar) = &*self.0; + let mut signaled = lock.lock().unwrap(); + *signaled = true; + cvar.notify_all(); + } + } +} + +#[cfg(any(windows, feature = "std"))] +pub use imp::*; diff --git a/third_party/windows-winui/windows-future/src/when.rs b/third_party/windows-winui/windows-future/src/when.rs new file mode 100644 index 0000000000..7f231fac4f --- /dev/null +++ b/third_party/windows-winui/windows-future/src/when.rs @@ -0,0 +1,41 @@ +use super::*; + +impl IAsyncAction { + /// Calls `op(result)` when the `IAsyncAction` completes. + pub fn when(&self, op: F) -> Result<()> + where + F: FnOnce(Result<()>) + Send + 'static, + { + Async::when(self, op) + } +} + +impl IAsyncOperation { + /// Calls `op(result)` when the `IAsyncOperation` completes. + pub fn when(&self, op: F) -> Result<()> + where + F: FnOnce(Result) + Send + 'static, + { + Async::when(self, op) + } +} + +impl IAsyncActionWithProgress

{ + /// Calls `op(result)` when the `IAsyncActionWithProgress

` completes. + pub fn when(&self, op: F) -> Result<()> + where + F: FnOnce(Result<()>) + Send + 'static, + { + Async::when(self, op) + } +} + +impl IAsyncOperationWithProgress { + /// Calls `op(result)` when the `IAsyncOperationWithProgress` completes. + pub fn when(&self, op: F) -> Result<()> + where + F: FnOnce(Result) + Send + 'static, + { + Async::when(self, op) + } +} diff --git a/third_party/windows-winui/windows-implement/Cargo.toml b/third_party/windows-winui/windows-implement/Cargo.toml new file mode 100644 index 0000000000..0947e74434 --- /dev/null +++ b/third_party/windows-winui/windows-implement/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "windows-implement" +version = "0.60.2" +edition = "2021" +rust-version = "1.74" +license = "MIT OR Apache-2.0" +description = "The implement macro for the Windows crates" +repository = "https://github.com/microsoft/windows-rs" +categories = ["os::windows-apis"] +readme = "readme.md" + +[dependencies] +proc-macro2 = { version = "1.0", default-features = false } +quote = { version = "1.0", default-features = false } +syn = { version = "2.0", default-features = false, features = ["parsing", "proc-macro", "printing", "full", "clone-impls"] } + +[dev-dependencies] +windows-core = { path = "../windows-core" } + +[package.metadata.docs.rs] +targets = [] + +[lib] +proc-macro = true diff --git a/third_party/windows-winui/windows-implement/license-apache-2.0 b/third_party/windows-winui/windows-implement/license-apache-2.0 new file mode 100644 index 0000000000..b5ed4ecec2 --- /dev/null +++ b/third_party/windows-winui/windows-implement/license-apache-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/windows-winui/windows-implement/license-mit b/third_party/windows-winui/windows-implement/license-mit new file mode 100644 index 0000000000..9e841e7a26 --- /dev/null +++ b/third_party/windows-winui/windows-implement/license-mit @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/third_party/windows-winui/windows-implement/readme.md b/third_party/windows-winui/windows-implement/readme.md new file mode 100644 index 0000000000..8d80eacb59 --- /dev/null +++ b/third_party/windows-winui/windows-implement/readme.md @@ -0,0 +1,3 @@ +## The implement macro for the Windows crates + +See [windows-core](https://crates.io/crates/windows-core) for more information. diff --git a/third_party/windows-winui/windows-implement/src/gen.rs b/third_party/windows-winui/windows-implement/src/gen.rs new file mode 100644 index 0000000000..b5a906e20c --- /dev/null +++ b/third_party/windows-winui/windows-implement/src/gen.rs @@ -0,0 +1,647 @@ +//! Generates output for the `implement` proc macro. +//! +//! Each function in this module focuses on generating one thing, or one kind of thing. +//! Each takes `ImplementInputs` as its input. `gen_all` calls all of the `gen_*` functions +//! and merges them into the final list of output items. +//! +//! `parse_quote` is used so that errors point at the specific generator rather than the +//! entire macro output. + +use super::*; +use quote::{quote, quote_spanned}; +use syn::{parse_quote, parse_quote_spanned}; + +/// Generates code for the `#[implements]` macro. +pub(crate) fn gen_all(inputs: &ImplementInputs) -> Vec { + let mut items: Vec = Vec::with_capacity(64); + + items.push(gen_original_impl(inputs)); + items.push(gen_impl_struct(inputs)); + items.push(gen_impl_deref(inputs)); + items.push(gen_impl_impl(inputs)); + items.push(gen_iunknown_impl(inputs)); + items.push(gen_impl_com_object_inner(inputs)); + items.push(gen_impl_compose(inputs)); + items.extend(gen_impl_from(inputs)); + items.extend(gen_impl_com_object_interfaces(inputs)); + + for (i, interface_chain) in inputs.interface_chains.iter().enumerate() { + items.push(gen_impl_as_impl(inputs, interface_chain, i)); + } + + items +} + +/// Generates an `impl` block for the original `Foo` type. +/// +/// This `impl` block will contain `into_outer` and `into_static` (if applicable). +fn gen_original_impl(inputs: &ImplementInputs) -> syn::Item { + let original_ident = &inputs.original_ident; + let generics = &inputs.generics; + let generics_idents = &inputs.generics_idents; + let constraints = &inputs.constraints; + + let mut output: syn::ItemImpl = parse_quote! { + impl #generics #original_ident::#generics_idents where #constraints {} + }; + + output.items.push(gen_into_outer(inputs)); + + // Static COM objects can't be generic: an open generic type has no known representation, + // and aggregated types currently rely on boxing during construction. + if !inputs.is_generic { + output.items.push(gen_into_static(inputs)); + } + + syn::Item::Impl(output) +} + +/// Generates the structure definition for the `Foo_Impl` type. +fn gen_impl_struct(inputs: &ImplementInputs) -> syn::Item { + let impl_ident = &inputs.impl_ident; + let generics = &inputs.generics; + let generics_idents = &inputs.generics_idents; + let constraints = &inputs.constraints; + let original_ident = &inputs.original_ident; + let vis = &inputs.original_type.vis; + + let mut impl_fields = quote! { + // Holds the inner non-delegating `IInspectable` when this type aggregates a composable + // WinRT class; otherwise stays `None`. `QueryInterface` falls through here when the + // requested IID is not handled locally. Wrapped in `ComposeBase` (`repr(transparent)` + // over `Option`) for `Sync`. + base: ::windows_core::ComposeBase, + identity: &'static ::windows_core::IInspectable_Vtbl, + }; + + for interface_chain in &inputs.interface_chains { + let vtbl_ty = interface_chain.implement.to_vtbl_ident(); + let chain_field_ident = &interface_chain.field_ident; + impl_fields.extend(quote! { + #chain_field_ident: &'static #vtbl_ty, + }); + } + + impl_fields.extend(quote! { + this: #original_ident::#generics_idents, + count: ::windows_core::imp::WeakRefCount, + }); + + parse_quote! { + #[repr(C)] + #[allow(non_camel_case_types)] + #vis struct #impl_ident #generics where #constraints { + #impl_fields + } + } +} + +/// Generates the implementation of `core::ops::Deref` for the generated `Foo_Impl` type. +fn gen_impl_deref(inputs: &ImplementInputs) -> syn::Item { + let generics = &inputs.generics; + let generics_idents = &inputs.generics_idents; + let constraints = &inputs.constraints; + let original_ident = &inputs.original_type.ident; + let impl_ident = &inputs.impl_ident; + + parse_quote! { + impl #generics ::core::ops::Deref for #impl_ident::#generics_idents where #constraints { + type Target = #original_ident::#generics_idents; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.this + } + } + } +} + +/// Generates an `impl` block for the generated `Foo_Impl` block. +/// +/// This generates: +/// +/// ```rust,ignore +/// const VTABLE_IDENTITY = IInspectable_Vtbl = ...; +/// const VTABLE_INTERFACE1_IFOO: IFoo_Vtbl = ...; +/// const VTABLE_INTERFACE2_IBAR: IBar_Vtbl = ...; +/// ``` +/// +/// Using associated constants works around limitations on generics in const contexts. +fn gen_impl_impl(inputs: &ImplementInputs) -> syn::Item { + let impl_ident = &inputs.impl_ident; + let generics = &inputs.generics; + let generics_idents = &inputs.generics_idents; + let constraints = &inputs.constraints; + + let mut output: syn::ItemImpl = parse_quote! { + impl #generics #impl_ident::#generics_idents where #constraints {} + }; + + // This is here so that IInspectable::GetRuntimeClassName can work properly. + // For a test case for this, see crates/tests/misc/component_client. + let identity_type = if let Some(first) = inputs.interface_chains.first() { + first.implement.to_ident() + } else { + quote! { ::windows_core::IInspectable } + }; + + output.items.push(parse_quote! { + const VTABLE_IDENTITY: ::windows_core::IInspectable_Vtbl = + ::windows_core::IInspectable_Vtbl::new::< + #impl_ident::#generics_idents, + #identity_type, + -1, + >(); + }); + + for (interface_index, interface_chain) in inputs.interface_chains.iter().enumerate() { + let vtbl_ty = interface_chain.implement.to_vtbl_ident(); + let vtable_const_ident = &interface_chain.vtable_const_ident; + + // Identity sits at -1 and each interface chain at -2, -3, ... in declaration order + // (offset by one for the leading `base` field). + let chain_offset_in_pointers: isize = -2 - interface_index as isize; + output.items.push(parse_quote! { + const #vtable_const_ident: #vtbl_ty = #vtbl_ty::new::< + #impl_ident::#generics_idents, + #chain_offset_in_pointers, + >(); + }); + } + + syn::Item::Impl(output) +} + +/// Generates the `IUnknownImpl` implementation for the `Foo_Impl` type. +fn gen_iunknown_impl(inputs: &ImplementInputs) -> syn::Item { + let generics = &inputs.generics; + let generics_idents = &inputs.generics_idents; + let constraints = &inputs.constraints; + let impl_ident = &inputs.impl_ident; + let original_ident = &inputs.original_type.ident; + + let trust_level = proc_macro2::Literal::usize_unsuffixed(inputs.trust_level); + + let mut output: syn::ItemImpl = parse_quote! { + impl #generics ::windows_core::IUnknownImpl for #impl_ident::#generics_idents where #constraints { + type Impl = #original_ident::#generics_idents; + + #[inline(always)] + fn get_impl(&self) -> &Self::Impl { + &self.this + } + + #[inline(always)] + fn get_impl_mut(&mut self) -> &mut Self::Impl { + &mut self.this + } + + #[inline(always)] + fn into_inner(self) -> Self::Impl { + self.this + } + + #[inline(always)] + fn AddRef(&self) -> u32 { + self.count.add_ref() + } + + #[inline(always)] + unsafe fn Release(self_: *mut Self) -> u32 { + let remaining = (*self_).count.release(); + if remaining == 0 { + _ = ::windows_core::imp::Box::from_raw(self_); + } + remaining + } + + #[inline(always)] + fn is_reference_count_one(&self) -> bool { + self.count.is_one() + } + + unsafe fn GetTrustLevel(&self, value: *mut i32) -> ::windows_core::HRESULT { + if value.is_null() { + return ::windows_core::imp::E_POINTER; + } + *value = #trust_level; + ::windows_core::HRESULT(0) + } + + fn to_object(&self) -> ::windows_core::ComObject { + self.count.add_ref(); + unsafe { + ::windows_core::ComObject::from_raw( + ::core::ptr::NonNull::new_unchecked(self as *const Self as *mut Self) + ) + } + } + } + }; + + let query_interface_fn = gen_query_interface(inputs); + output.items.push(syn::ImplItem::Fn(query_interface_fn)); + + syn::Item::Impl(output) +} + +/// Generates the implementation of `ComObjectInner`. +fn gen_impl_com_object_inner(inputs: &ImplementInputs) -> syn::Item { + let original_ident = &inputs.original_type.ident; + let generics = &inputs.generics; + let generics_idents = &inputs.generics_idents; + let constraints = &inputs.constraints; + let impl_ident = &inputs.impl_ident; + + parse_quote! { + impl #generics ::windows_core::ComObjectInner for #original_ident::#generics_idents where #constraints { + type Outer = #impl_ident::#generics_idents; + + // Assembles the boxed COM object. Move the value into a heap allocation and return + // only a `ComObject` reference, never an owned `Foo_Impl`: exposing an owned + // `Foo_Impl` to safe code would be unsound because of the reference-count + // adjustments it performs. + + fn into_object(self) -> ::windows_core::ComObject { + let boxed = ::windows_core::imp::Box::<#impl_ident::#generics_idents>::new(self.into_outer()); + unsafe { + let ptr = ::windows_core::imp::Box::into_raw(boxed); + ::windows_core::ComObject::from_raw( + ::core::ptr::NonNull::new_unchecked(ptr) + ) + } + } + } + } +} + +/// Generates the `Compose` implementation that lets `Foo` be used as the Rust derived +/// implementation in a composable WinRT runtime class. +/// +/// Assembles an `IInspectable` for the freshly constructed implementation and then computes +/// a mutable reference into the `base` field of its `Foo_Impl` (offset 0, immediately +/// before `identity`). The composable factory writes the inner non-delegating +/// `IInspectable` back through that reference. +fn gen_impl_compose(inputs: &ImplementInputs) -> syn::Item { + let original_ident = &inputs.original_type.ident; + let generics = &inputs.generics; + let generics_idents = &inputs.generics_idents; + let constraints = &inputs.constraints; + + parse_quote! { + impl #generics ::windows_core::Compose for #original_ident::#generics_idents where #constraints { + unsafe fn compose<'a>( + implementation: Self, + ) -> (::windows_core::IInspectable, &'a mut ::core::option::Option<::windows_core::IInspectable>) { + unsafe { + let inspectable: ::windows_core::IInspectable = implementation.into(); + // The IInspectable points at the `identity` field; `base` lives at + // the previous pointer-sized slot, since `ComposeBase` is + // `repr(transparent)` over `Option`. + let identity_ptr: *mut ::core::ffi::c_void = ::windows_core::Interface::as_raw(&inspectable); + let base_ptr = (identity_ptr as *mut *mut ::core::ffi::c_void).sub(1) + as *mut ::core::option::Option<::windows_core::IInspectable>; + (inspectable, &mut *base_ptr) + } + } + } + } +} + +/// Generates the `query_interface` method. +fn gen_query_interface(inputs: &ImplementInputs) -> syn::ImplItemFn { + let queries = inputs.interface_chains.iter().map(|interface_chain| { + let chain_ty = interface_chain.implement.to_vtbl_ident(); + let chain_field = &interface_chain.field_ident; + quote_spanned! { + interface_chain.implement.span => + if #chain_ty::matches(&iid) { + break 'found &self.#chain_field as *const _ as *const ::core::ffi::c_void; + } + } + }); + + // Dynamic casting requires that the object not contain non-static lifetimes. + let enable_dyn_casting = inputs.original_type.generics.lifetimes().count() == 0; + let dynamic_cast_query = if enable_dyn_casting { + quote! { + if iid == ::windows_core::DYNAMIC_CAST_IID { + // DYNAMIC_CAST_IID is special. We _do not_ increase the reference count for this pseudo-interface. + // Also, instead of returning an interface pointer, we simply write the `&dyn Any` directly to the + // 'interface' pointer. Since the size of `&dyn Any` is 2 pointers, not one, the caller must be + // prepared for this. This is not a normal QueryInterface call. + // + // See the `Interface::cast_to_any` method, which is the only caller that should use DYNAMIC_CAST_ID. + (interface as *mut *const dyn core::any::Any).write(self as &dyn ::core::any::Any as *const dyn ::core::any::Any); + return ::windows_core::HRESULT(0); + } + } + } else { + quote!() + }; + + let identity_query = if inputs.agile { + quote! { + if iid == <::windows_core::IUnknown as ::windows_core::Interface>::IID + || iid == <::windows_core::IInspectable as ::windows_core::Interface>::IID + || iid == <::windows_core::imp::IAgileObject as ::windows_core::Interface>::IID { + break 'found &self.identity as *const _ as *const ::core::ffi::c_void; + } + } + } else { + quote! { + if iid == <::windows_core::IUnknown as ::windows_core::Interface>::IID + || iid == <::windows_core::IInspectable as ::windows_core::Interface>::IID { + break 'found &self.identity as *const _ as *const ::core::ffi::c_void; + } + } + }; + + let marshal_query = if inputs.agile { + quote! { + #[cfg(windows)] + if iid == <::windows_core::imp::IMarshal as ::windows_core::Interface>::IID { + return ::windows_core::imp::marshaler(self.to_interface(), interface); + } + } + } else { + quote! {} + }; + + let tear_off_query = quote! { + let tear_off_ptr = self.count.query(&iid, &self.identity as *const _ as *mut _); + if !tear_off_ptr.is_null() { + *interface = tear_off_ptr; + return ::windows_core::HRESULT(0); + } + }; + + // When this implementation aggregates a composable WinRT class, an unrecognized IID + // is forwarded to the inner non-delegating `IInspectable` stored in `self.base`. + let aggregation_query = quote! { + if let ::core::option::Option::Some(base) = self.base.as_option() { + return ::windows_core::Interface::query(base, &iid as *const ::windows_core::GUID, interface); + } + }; + + parse_quote! { + unsafe fn QueryInterface( + &self, + iid: *const ::windows_core::GUID, + interface: *mut *mut ::core::ffi::c_void, + ) -> ::windows_core::HRESULT { + unsafe { + if iid.is_null() || interface.is_null() { + return ::windows_core::imp::E_POINTER; + } + + let iid = *iid; + + let interface_ptr: *const ::core::ffi::c_void = 'found: { + #identity_query + #(#queries)* + #marshal_query + #dynamic_cast_query + #tear_off_query + #aggregation_query + + *interface = ::core::ptr::null_mut(); + return ::windows_core::imp::E_NOINTERFACE; + }; + + debug_assert!(!interface_ptr.is_null()); + *interface = interface_ptr as *mut ::core::ffi::c_void; + self.count.add_ref(); + return ::windows_core::HRESULT(0); + } + } + } +} + +/// Generates the `T::into_outer` function. This function is part of how we construct a +/// `ComObject` from a `T`. +fn gen_into_outer(inputs: &ImplementInputs) -> syn::ImplItem { + let generics_idents = &inputs.generics_idents; + let impl_ident = &inputs.impl_ident; + + let mut initializers = quote! { + // Empty until populated by `Compose::compose` for aggregating types. + base: ::windows_core::ComposeBase::new(), + identity: &#impl_ident::#generics_idents::VTABLE_IDENTITY, + }; + + for interface_chain in &inputs.interface_chains { + let vtbl_field_ident = &interface_chain.field_ident; + let vtable_const_ident = &interface_chain.vtable_const_ident; + + initializers.extend(quote_spanned! { + interface_chain.implement.span => + #vtbl_field_ident: &#impl_ident::#generics_idents::#vtable_const_ident, + }); + } + + // If the type is generic then into_outer() cannot be a const fn. + let maybe_const = if inputs.is_generic { + quote!() + } else { + quote!(const) + }; + + parse_quote! { + // Constructs the "outer" object. Used only by the implementation of the outer object, + // never by application code. + // + // The callers of this function (`into_static` and `into_object`) are both responsible + // for maintaining one of our invariants: application code never has an owned instance + // of the outer (implementation) type. `into_static` maintains this invariant by + // returning a wrapped `StaticComObject` value, which owns its contents but never gives + // application code a way to mutably access them. This prevents the refcount-shearing + // problem. + // + // TODO: Make it impossible for app code to call this function, by placing it in a + // module and marking this as private to the module. + #[inline(always)] + #maybe_const fn into_outer(self) -> #impl_ident::#generics_idents { + #impl_ident::#generics_idents { + #initializers + count: ::windows_core::imp::WeakRefCount::new(), + this: self, + } + } + } +} + +/// Generates the `T::into_static` function. This function is part of how we construct a +/// `StaticComObject` from a `T`. +fn gen_into_static(inputs: &ImplementInputs) -> syn::ImplItem { + assert!(!inputs.is_generic); + parse_quote! { + /// This converts a partially-constructed COM object (containing application state but + /// without vtable and reference count yet set up) into a `StaticComObject`. This allows + /// the COM object to be stored in static (global) variables. + pub const fn into_static(self) -> ::windows_core::StaticComObject { + ::windows_core::StaticComObject::from_outer(self.into_outer()) + } + } +} + +/// Generates `From`-based conversions. +/// +/// These conversions convert from the user's type `T` to `ComObject` or to an interface +/// implemented by `T`. These conversions are shorthand for calling `ComObject::new(value)`. +/// +/// We can only generate conversions from `T` to the roots of each interface chain. We can't +/// generate `From` conversions from `T` to an interface that is inherited by an interface chain, +/// because this proc macro does not have access to any information about the inheritance chain +/// of interfaces that are referenced. +/// +/// For example: +/// +/// ```rust,ignore +/// #[implement(IFoo3)] +/// struct MyType; +/// ``` +/// +/// If `IFoo3` inherits from `IFoo2`, then this code will _not_ generate a conversion for `IFoo2`. +/// However, user code can still do this: +/// +/// ```rust,ignore +/// let ifoo2 = IFoo3::from(MyType).into(); +/// ``` +/// +/// This works because the `IFoo3` type has an `Into` impl for `IFoo2`. +fn gen_impl_from(inputs: &ImplementInputs) -> Vec { + let mut items = Vec::new(); + + let original_ident = &inputs.original_type.ident; + let generics = &inputs.generics; + let generics_idents = &inputs.generics_idents; + let constraints = &inputs.constraints; + + items.push(parse_quote! { + impl #generics ::core::convert::From<#original_ident::#generics_idents> for ::windows_core::IUnknown where #constraints { + #[inline(always)] + fn from(this: #original_ident::#generics_idents) -> Self { + let com_object = ::windows_core::ComObject::new(this); + com_object.into_interface() + } + } + }); + + items.push(parse_quote! { + impl #generics ::core::convert::From<#original_ident::#generics_idents> for ::windows_core::IInspectable where #constraints { + #[inline(always)] + fn from(this: #original_ident::#generics_idents) -> Self { + let com_object = ::windows_core::ComObject::new(this); + com_object.into_interface() + } + } + }); + + for interface_chain in &inputs.interface_chains { + let interface_ident = interface_chain.implement.to_ident(); + + items.push(parse_quote_spanned! { + interface_chain.implement.span => + impl #generics ::core::convert::From<#original_ident::#generics_idents> for #interface_ident where #constraints { + #[inline(always)] + fn from(this: #original_ident::#generics_idents) -> Self { + let com_object = ::windows_core::ComObject::new(this); + com_object.into_interface() + } + } + }); + } + + items +} + +/// Generates the `ComObjectInterface` implementation for each interface chain. +/// +/// Each of these `impl` blocks says "this COM object implements this COM interface". +/// It allows the `ComObject` type to do conversions from the `ComObject` to `IFoo` instances, +/// _without_ doing a `QueryInterface` call. +fn gen_impl_com_object_interfaces(inputs: &ImplementInputs) -> Vec { + let mut items = Vec::new(); + + let generics = &inputs.generics; + let generics_idents = &inputs.generics_idents; + let constraints = &inputs.constraints; + let impl_ident = &inputs.impl_ident; + + items.push(parse_quote! { + impl #generics ::windows_core::ComObjectInterface<::windows_core::IUnknown> for #impl_ident::#generics_idents where #constraints { + #[inline(always)] + fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, ::windows_core::IUnknown> { + unsafe { + let interface_ptr = &self.identity; + ::core::mem::transmute(interface_ptr) + } + } + } + }); + + items.push(parse_quote! { + impl #generics ::windows_core::ComObjectInterface<::windows_core::IInspectable> for #impl_ident::#generics_idents where #constraints { + #[inline(always)] + fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, ::windows_core::IInspectable> { + unsafe { + let interface_ptr = &self.identity; + ::core::mem::transmute(interface_ptr) + } + } + } + }); + + for interface_chain in &inputs.interface_chains { + let chain_field = &interface_chain.field_ident; + let interface_ident = interface_chain.implement.to_ident(); + + items.push(parse_quote_spanned! { + interface_chain.implement.span => + #[allow(clippy::needless_lifetimes)] + impl #generics ::windows_core::ComObjectInterface<#interface_ident> for #impl_ident::#generics_idents where #constraints { + #[inline(always)] + fn as_interface_ref(&self) -> ::windows_core::InterfaceRef<'_, #interface_ident> { + unsafe { + ::core::mem::transmute(&self.#chain_field) + } + } + } + }); + } + + items +} + +/// Generates the implementation of the `AsImpl` trait for a given interface chain. +fn gen_impl_as_impl( + inputs: &ImplementInputs, + interface_chain: &InterfaceChain, + interface_chain_index: usize, +) -> syn::Item { + let generics = &inputs.generics; + let generics_idents = &inputs.generics_idents; + let constraints = &inputs.constraints; + let interface_ident = interface_chain.implement.to_ident(); + let original_ident = &inputs.original_type.ident; + let impl_ident = &inputs.impl_ident; + + parse_quote_spanned! { + interface_chain.implement.span => + impl #generics ::windows_core::AsImpl<#original_ident::#generics_idents> for #interface_ident where #constraints { + // SAFETY: the offset is guaranteed to be in bounds, and the implementation struct + // is guaranteed to live at least as long as `self`. + #[inline(always)] + unsafe fn as_impl_ptr(&self) -> ::core::ptr::NonNull<#original_ident::#generics_idents> { + unsafe { + let this = ::windows_core::Interface::as_raw(self); + // Subtract the vtable offset plus 2 (the `base` and `identity` fields) + // to reach the impl struct. + let this = (this as *mut *mut ::core::ffi::c_void).sub(2 + #interface_chain_index) as *mut #impl_ident::#generics_idents; + ::core::ptr::NonNull::new_unchecked(::core::ptr::addr_of!((*this).this) as *const #original_ident::#generics_idents as *mut #original_ident::#generics_idents) + } + } + } + } +} diff --git a/third_party/windows-winui/windows-implement/src/lib.rs b/third_party/windows-winui/windows-implement/src/lib.rs new file mode 100644 index 0000000000..afc9d6ec7d --- /dev/null +++ b/third_party/windows-winui/windows-implement/src/lib.rs @@ -0,0 +1,471 @@ +//! Implement COM interfaces for Rust types. +//! +//! Take a look at [macro@implement] for an example. +//! +//! Learn more about Rust for Windows here: +//! +//! # Architecture +//! +//! ## Naming conventions +//! +//! - `Foo` — the user's struct, passed in with `#[implement(...)]`. +//! - `Foo_Impl` — the generated outer type that wraps `Foo` and adds vtable pointers, a +//! reference count, and the identity vtable pointer. Users implement `IFoo_Impl` +//! (generated by `#[interface]`) for `Foo_Impl`, not for `Foo`. +//! +//! ## Vtable layout +//! +//! `Foo_Impl` is a `#[repr(C)]` struct with these fields, in order: +//! +//! 1. `base` — `Option` slot for COM aggregation (composition). Holds the +//! inner non-delegating `IInspectable` written back by a composable factory; otherwise +//! `None`. At offset 0 so that the vtable pointers below it occupy negative offsets +//! relative to the COM pointer. +//! 2. `identity` — a `&'static IInspectable_Vtbl` (offset -1) acting as the IUnknown / +//! IInspectable identity pointer. +//! 3. One `&'static ::Vtable` per interface chain in declaration order +//! (offsets -2, -3, …). +//! 4. `this` — the user's `Foo` value. +//! 5. `count` — a `WeakRefCount`. +//! +//! When a COM caller holds an `IFoo*`, it points into the vtable-pointer field at offset +//! -k. To recover the `Foo_Impl` pointer, code in `gen_query_interface` and +//! `gen_impl_as_impl` subtracts `2 + k` pointer-sized units (the `+ 2` accounts for the +//! leading `base` and `identity` fields). +//! +//! ## Relationship to `windows-interface` +//! +//! `#[interface]` (in `windows-interface`) generates: +//! - The `IFoo` struct (a transparent wrapper around the parent interface). +//! - The `IFoo_Vtbl` vtable struct with a `new::()` constructor. +//! - The `IFoo_Impl` trait that users implement on `Foo_Impl`. +//! +//! `#[implement]` (this crate) generates: +//! - The `Foo_Impl` wrapper struct with vtable fields, `this`, and `count`. +//! - Implementations of `IUnknownImpl`, `ComObjectInner`, `ComObjectInterface`, `AsImpl`. +//! - `From` conversions to each implemented interface. +//! +//! Both macros must agree on the vtable layout and the `_Impl` naming convention. + +use quote::{quote, ToTokens}; + +mod r#gen; +use r#gen::gen_all; + +#[cfg(test)] +mod tests; + +/// Implements one or more COM interfaces. +/// +/// # Example +/// ```rust,no_run +/// use windows_core::*; +/// +/// #[interface("094d70d6-5202-44b8-abb8-43860da5aca2")] +/// unsafe trait IValue: IUnknown { +/// fn GetValue(&self, value: *mut i32) -> HRESULT; +/// } +/// +/// #[implement(IValue)] +/// struct Value(i32); +/// +/// impl IValue_Impl for Value_Impl { +/// unsafe fn GetValue(&self, value: *mut i32) -> HRESULT { +/// *value = self.0; +/// HRESULT(0) +/// } +/// } +/// +/// let object: IValue = Value(123).into(); +/// // Call interface methods... +/// ``` +#[proc_macro_attribute] +pub fn implement( + attributes: proc_macro::TokenStream, + type_tokens: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + implement_core(attributes.into(), type_tokens.into()).into() +} + +fn implement_core( + attributes: proc_macro2::TokenStream, + item_tokens: proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + let attributes = match syn::parse2::(attributes) { + Ok(a) => a, + Err(e) => return e.into_compile_error(), + }; + let original_type = match syn::parse2::(item_tokens) { + Ok(t) => t, + Err(e) => return e.into_compile_error(), + }; + + let inputs = ImplementInputs { + original_ident: original_type.ident.clone(), + interface_chains: convert_implements_to_interface_chains(attributes.implement), + trust_level: attributes.trust_level, + agile: attributes.agile, + impl_ident: quote::format_ident!("{}_Impl", &original_type.ident), + constraints: { + if let Some(where_clause) = &original_type.generics.where_clause { + where_clause.predicates.to_token_stream() + } else { + quote!() + } + }, + generics: if !original_type.generics.params.is_empty() { + let mut params = quote! {}; + original_type.generics.params.to_tokens(&mut params); + quote! { <#params> } + } else { + quote! { <> } + }, + generics_idents: if !original_type.generics.params.is_empty() { + let idents: Vec<_> = original_type + .generics + .params + .iter() + .map(|param| { + let mut ident = quote! {}; + match param { + syn::GenericParam::Type(ty) => ty.ident.to_tokens(&mut ident), + syn::GenericParam::Lifetime(lt) => lt.lifetime.to_tokens(&mut ident), + syn::GenericParam::Const(cnst) => cnst.ident.to_tokens(&mut ident), + }; + + ident + }) + .collect(); + quote! { <#(#idents),*> } + } else { + quote! { <> } + }, + is_generic: !original_type.generics.params.is_empty(), + original_type, + }; + + let items = gen_all(&inputs); + let mut tokens = inputs.original_type.into_token_stream(); + for item in items { + tokens.extend(item.into_token_stream()); + } + + tokens +} + +/// This provides the inputs to the `gen_*` functions, which generate the proc macro output. +struct ImplementInputs { + /// The user's type that was marked with `#[implement]`. + original_type: syn::ItemStruct, + + /// The identifier for the user's original type definition. + original_ident: syn::Ident, + + /// The list of interface chains that this type implements. + interface_chains: Vec, + + /// The "trust level", which is returned by `IInspectable::GetTrustLevel`. + trust_level: usize, + + /// Determines whether `IAgileObject` and `IMarshal` are implemented automatically. + agile: bool, + + /// The identifier of the `Foo_Impl` type. + impl_ident: syn::Ident, + + /// The list of constraints needed for this `Foo_Impl` type. + constraints: proc_macro2::TokenStream, + + /// The list of generic parameters for this `Foo_Impl` type, including `<` and `>`. + /// If there are no generics, this contains `<>`. + generics: proc_macro2::TokenStream, + + /// The list of generic parameters without any bounds, e.g. ``. + /// This is used for things like `impl IInterface for Foo`. + generics_idents: proc_macro2::TokenStream, + + /// True if the user type has any generic parameters. + is_generic: bool, +} + +/// Describes one COM interface chain. +struct InterfaceChain { + /// The name of the field for the vtable chain, e.g. `interface4_ifoo`. + field_ident: syn::Ident, + + /// The name of the associated constant item for the vtable chain's initializer, + /// e.g. `INTERFACE4_IFOO_VTABLE`. + vtable_const_ident: syn::Ident, + + implement: ImplementType, +} + +struct ImplementType { + type_name: String, + generics: Vec, + + /// The best span for diagnostics. + span: proc_macro2::Span, +} + +impl ImplementType { + fn to_ident(&self) -> proc_macro2::TokenStream { + let type_name = syn::parse_str::(&self.type_name) + .expect("Invalid token stream"); + let generics = self.generics.iter().map(|g| g.to_ident()); + quote! { #type_name<#(#generics,)*> } + } + fn to_vtbl_ident(&self) -> proc_macro2::TokenStream { + let ident = self.to_ident(); + quote! { + <#ident as ::windows_core::Interface>::Vtable + } + } +} + +#[derive(Default)] +struct ImplementAttributes { + pub implement: Vec, + pub trust_level: usize, + pub agile: bool, +} + +impl syn::parse::Parse for ImplementAttributes { + fn parse(cursor: syn::parse::ParseStream) -> syn::parse::Result { + let mut input = Self { + agile: true, + ..Default::default() + }; + + while !cursor.is_empty() { + input.parse_implement(cursor)?; + } + + Ok(input) + } +} + +impl ImplementAttributes { + fn parse_implement(&mut self, cursor: syn::parse::ParseStream) -> syn::parse::Result<()> { + let tree = cursor.parse::()?; + self.walk_implement(&tree, &mut String::new())?; + + if !cursor.is_empty() { + cursor.parse::()?; + } + + Ok(()) + } + + fn walk_implement( + &mut self, + tree: &UseTree2, + namespace: &mut String, + ) -> syn::parse::Result<()> { + match tree { + UseTree2::Path(input) => { + if !namespace.is_empty() { + namespace.push_str("::"); + } + + namespace.push_str(&input.ident.to_string()); + self.walk_implement(&input.tree, namespace)?; + } + UseTree2::Name(_) => { + self.implement.push(tree.to_element_type(namespace)?); + } + UseTree2::Group(input) => { + for tree in &input.items { + self.walk_implement(tree, namespace)?; + } + } + UseTree2::TrustLevel(input) => self.trust_level = *input, + UseTree2::Agile(agile) => self.agile = *agile, + } + + Ok(()) + } +} + +enum UseTree2 { + Path(UsePath2), + Name(UseName2), + Group(UseGroup2), + TrustLevel(usize), + Agile(bool), +} + +impl UseTree2 { + fn to_element_type(&self, namespace: &mut String) -> syn::parse::Result { + match self { + Self::Path(input) => { + if !namespace.is_empty() { + namespace.push_str("::"); + } + + namespace.push_str(&input.ident.to_string()); + input.tree.to_element_type(namespace) + } + Self::Name(input) => { + let mut type_name = input.ident.to_string(); + let span = input.ident.span(); + + if !namespace.is_empty() { + type_name = format!("{namespace}::{type_name}"); + } + + let mut generics = vec![]; + + for g in &input.generics { + generics.push(g.to_element_type(&mut String::new())?); + } + + Ok(ImplementType { + type_name, + generics, + span, + }) + } + Self::Group(input) => Err(syn::parse::Error::new( + input.brace_token.span.join(), + "Syntax not supported", + )), + _ => unimplemented!(), + } + } +} + +struct UsePath2 { + pub ident: syn::Ident, + pub tree: Box, +} + +struct UseName2 { + pub ident: syn::Ident, + pub generics: Vec, +} + +struct UseGroup2 { + pub brace_token: syn::token::Brace, + pub items: syn::punctuated::Punctuated, +} + +impl syn::parse::Parse for UseTree2 { + fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { + let lookahead = input.lookahead1(); + if lookahead.peek(syn::Ident) { + use syn::ext::IdentExt; + let ident = input.call(syn::Ident::parse_any)?; + if input.peek(syn::Token![::]) { + input.parse::()?; + Ok(Self::Path(UsePath2 { + ident, + tree: Box::new(input.parse()?), + })) + } else if input.peek(syn::Token![=]) { + if ident == "TrustLevel" { + input.parse::()?; + let span = input.span(); + let value = input.call(syn::Ident::parse_any)?; + match value.to_string().as_str() { + "Partial" => Ok(Self::TrustLevel(1)), + "Full" => Ok(Self::TrustLevel(2)), + _ => Err(syn::parse::Error::new( + span, + "`TrustLevel` must be `Partial` or `Full`", + )), + } + } else if ident == "Agile" { + input.parse::()?; + let span = input.span(); + let value = input.call(syn::Ident::parse_any)?; + match value.to_string().as_str() { + "true" => Ok(Self::Agile(true)), + "false" => Ok(Self::Agile(false)), + _ => Err(syn::parse::Error::new( + span, + "`Agile` must be `true` or `false`", + )), + } + } else { + Err(syn::parse::Error::new( + ident.span(), + "Unrecognized key-value pair", + )) + } + } else { + let generics = if input.peek(syn::Token![<]) { + input.parse::()?; + let mut generics = Vec::new(); + loop { + generics.push(input.parse::()?); + + if input.parse::().is_err() { + break; + } + } + input.parse::]>()?; + generics + } else { + Vec::new() + }; + + Ok(Self::Name(UseName2 { ident, generics })) + } + } else if lookahead.peek(syn::token::Brace) { + let content; + let brace_token = syn::braced!(content in input); + let items = content.parse_terminated(Self::parse, syn::Token![,])?; + + Ok(Self::Group(UseGroup2 { brace_token, items })) + } else { + Err(lookahead.error()) + } + } +} + +fn convert_implements_to_interface_chains(implements: Vec) -> Vec { + let mut chains = Vec::with_capacity(implements.len()); + + for (i, implement) in implements.into_iter().enumerate() { + // Field/const naming uses `i + 1` because interface 0 is the identity interface. + let mut ident_string = format!("interface{}", i + 1); + + let suffix = get_interface_ident_suffix(&implement.type_name); + if !suffix.is_empty() { + ident_string.push('_'); + ident_string.push_str(&suffix); + } + let field_ident = syn::Ident::new(&ident_string, implement.span); + + let mut vtable_const_string = ident_string.clone(); + vtable_const_string.make_ascii_uppercase(); + vtable_const_string.insert_str(0, "VTABLE_"); + let vtable_const_ident = syn::Ident::new(&vtable_const_string, implement.span); + + chains.push(InterfaceChain { + implement, + field_ident, + vtable_const_ident, + }); + } + + chains +} + +fn get_interface_ident_suffix(type_name: &str) -> String { + let mut suffix = String::new(); + for c in type_name.chars() { + let c = c.to_ascii_lowercase(); + + if suffix.len() >= 20 { + break; + } + + if c.is_ascii_alphanumeric() { + suffix.push(c); + } + } + + suffix +} diff --git a/third_party/windows-winui/windows-implement/src/tests.rs b/third_party/windows-winui/windows-implement/src/tests.rs new file mode 100644 index 0000000000..e54e285905 --- /dev/null +++ b/third_party/windows-winui/windows-implement/src/tests.rs @@ -0,0 +1,286 @@ +//! Tests for the `#[implement]` macro that verify the generated code structure. +//! +//! These tests call `implement_core` directly and check that the formatted output contains +//! the expected declarations and trait implementations. Any change to the code generator +//! that silently removes or renames a key item will be caught as a test failure. +//! +//! To inspect the full formatted output of a test, run with `--nocapture`: +//! +//! ```text +//! cargo test -p windows-implement --lib -- --nocapture --test-threads=1 +//! ``` + +use std::io::{Read, Write}; +use std::process::{Command, Stdio}; + +use proc_macro2::TokenStream; +use quote::quote; + +fn implement(attributes: TokenStream, item_tokens: TokenStream) -> String { + let out_tokens = crate::implement_core(attributes, item_tokens); + let tokens_string = out_tokens.to_string(); + + let out_string = rustfmt(&tokens_string); + println!("// output of #[implement] :"); + println!(); + println!("{out_string}"); + out_string +} + +fn check(output: &str, expected_items: &[&str]) { + for item in expected_items { + assert!( + output.contains(item), + "output does not contain expected item:\n expected: {item}\n\nfull output:\n{output}" + ); + } +} + +fn rustfmt(input: &str) -> String { + let mut rustfmt = Command::new("rustfmt"); + + rustfmt.stdin(Stdio::piped()); + rustfmt.stdout(Stdio::piped()); + rustfmt.stderr(Stdio::inherit()); + + let mut child = match rustfmt.spawn() { + Ok(c) => c, + Err(e) => { + eprintln!("failed to spawn rustfmt: {e:?}"); + return input.to_string(); + } + }; + + let mut stdout = child.stdout.take().unwrap(); + + // spawn thread to read stdout + let stdout_thread = std::thread::spawn(move || { + let mut buf = String::new(); + stdout.read_to_string(&mut buf).unwrap(); + buf + }); + + // write unformatted into stdin + let mut stdin = child.stdin.take().unwrap(); + stdin.write_all(input.as_bytes()).unwrap(); + drop(stdin); + + let stdout_string: String = stdout_thread.join().unwrap(); + + let exit = child.wait().unwrap(); + if !exit.success() { + eprintln!("rustfmt terminated with failure status code"); + return input.to_string(); + } + + stdout_string +} + +#[test] +fn simple_type() { + let output = implement( + quote!(IFoo), + quote! { + struct Foo { + x: u32, + } + }, + ); + check( + &output, + &[ + "struct Foo_Impl", + "impl ::core::ops::Deref for Foo_Impl", + "impl ::windows_core::IUnknownImpl for Foo_Impl", + "impl ::windows_core::ComObjectInner for Foo", + "impl ::core::convert::From for ::windows_core::IUnknown", + "impl ::core::convert::From for ::windows_core::IInspectable", + "impl ::core::convert::From for IFoo", + "impl ::windows_core::ComObjectInterface for Foo_Impl", + "impl ::windows_core::AsImpl for IFoo", + ], + ); +} + +#[test] +fn zero_sized_type() { + let output = implement( + quote!(IFoo), + quote! { + struct Foo; + }, + ); + check( + &output, + &[ + "struct Foo_Impl", + "impl ::windows_core::IUnknownImpl for Foo_Impl", + "impl ::windows_core::ComObjectInner for Foo", + "impl ::core::convert::From for IFoo", + "impl ::windows_core::AsImpl for IFoo", + ], + ); +} + +#[test] +fn no_interfaces() { + let output = implement( + quote!(), + quote! { + struct Foo {} + }, + ); + check( + &output, + &[ + "struct Foo_Impl", + "impl ::windows_core::IUnknownImpl for Foo_Impl", + "impl ::windows_core::ComObjectInner for Foo", + "impl ::core::convert::From for ::windows_core::IUnknown", + ], + ); + // No interface-specific items should be present. + assert!( + !output.contains("impl ::core::convert::From for IFoo"), + "no_interfaces output unexpectedly contains From for IFoo" + ); + assert!( + !output.contains("impl ::windows_core::AsImpl"), + "no_interfaces output unexpectedly contains AsImpl" + ); +} + +#[test] +fn generic_no_lifetime() { + let output = implement( + quote!(IAsyncOperationWithProgress, IAsyncInfo), + quote! { + struct OperationWithProgress(SyncState>) + where + P: RuntimeType + 'static; + + }, + ); + check( + &output, + &[ + "struct OperationWithProgress_Impl", + "impl", + "::windows_core::IUnknownImpl for OperationWithProgress_Impl", + "impl", + "::windows_core::ComObjectInner for OperationWithProgress", + "impl", + "::core::convert::From { + pub x: &'a [u8], + } + }, + ); + check( + &output, + &[ + "struct Foo_Impl", + "impl", + "::windows_core::IUnknownImpl for Foo_Impl", + "impl", + "::windows_core::ComObjectInner for Foo", + ], + ); + // Lifetime generics should also suppress into_static. + assert!( + !output.contains("into_static"), + "generic_with_lifetime output unexpectedly contains into_static" + ); +} + +#[test] +fn tuple_type() { + let output = implement( + quote!(IFoo), + quote! { + struct Foo(pub i32); + }, + ); + check( + &output, + &[ + "struct Foo_Impl", + "impl ::windows_core::IUnknownImpl for Foo_Impl", + "impl ::windows_core::ComObjectInner for Foo", + "impl ::core::convert::From for IFoo", + "impl ::windows_core::AsImpl for IFoo", + ], + ); +} + +#[test] +fn two_interfaces() { + let output = implement( + quote!(IFoo, IBar), + quote! { + struct Baz; + }, + ); + check( + &output, + &[ + "struct Baz_Impl", + "impl ::windows_core::IUnknownImpl for Baz_Impl", + "impl ::core::convert::From for IFoo", + "impl ::core::convert::From for IBar", + "impl ::windows_core::ComObjectInterface for Baz_Impl", + "impl ::windows_core::ComObjectInterface for Baz_Impl", + "impl ::windows_core::AsImpl for IFoo", + "impl ::windows_core::AsImpl for IBar", + ], + ); +} + +#[test] +fn not_agile() { + let output = implement( + quote!(IFoo, Agile = false), + quote! { + struct Foo; + }, + ); + // When Agile=false, IAgileObject should NOT appear in QueryInterface. + assert!( + !output.contains("IAgileObject"), + "not_agile output unexpectedly contains IAgileObject" + ); +} + +#[test] +fn namespaced_interface() { + let output = implement( + quote!(Windows::Win32::IFoo), + quote! { + struct Foo; + }, + ); + check( + &output, + &[ + "struct Foo_Impl", + "impl ::windows_core::AsImpl for Windows::Win32::IFoo", + ], + ); +} diff --git a/third_party/windows-winui/windows-interface/Cargo.toml b/third_party/windows-winui/windows-interface/Cargo.toml new file mode 100644 index 0000000000..845c68619e --- /dev/null +++ b/third_party/windows-winui/windows-interface/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "windows-interface" +version = "0.59.3" +edition = "2021" +rust-version = "1.74" +license = "MIT OR Apache-2.0" +description = "The interface macro for the Windows crates" +repository = "https://github.com/microsoft/windows-rs" +categories = ["os::windows-apis"] +readme = "readme.md" + +[dependencies] +proc-macro2 = { version = "1.0", default-features = false } +quote = { version = "1.0", default-features = false } +syn = { version = "2.0", default-features = false, features = ["parsing", "proc-macro", "printing", "full", "clone-impls"] } + +[dev-dependencies] +windows-core = { path = "../windows-core" } + +[package.metadata.docs.rs] +targets = [] + +[lib] +proc-macro = true diff --git a/third_party/windows-winui/windows-interface/license-apache-2.0 b/third_party/windows-winui/windows-interface/license-apache-2.0 new file mode 100644 index 0000000000..b5ed4ecec2 --- /dev/null +++ b/third_party/windows-winui/windows-interface/license-apache-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/windows-winui/windows-interface/license-mit b/third_party/windows-winui/windows-interface/license-mit new file mode 100644 index 0000000000..9e841e7a26 --- /dev/null +++ b/third_party/windows-winui/windows-interface/license-mit @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/third_party/windows-winui/windows-interface/readme.md b/third_party/windows-winui/windows-interface/readme.md new file mode 100644 index 0000000000..1160db5ef8 --- /dev/null +++ b/third_party/windows-winui/windows-interface/readme.md @@ -0,0 +1,3 @@ +## The interface macro for the Windows crates + +See [windows-core](https://crates.io/crates/windows-core) for more information. diff --git a/third_party/windows-winui/windows-interface/src/gen.rs b/third_party/windows-winui/windows-interface/src/gen.rs new file mode 100644 index 0000000000..6f89892d19 --- /dev/null +++ b/third_party/windows-winui/windows-interface/src/gen.rs @@ -0,0 +1,519 @@ +//! Code generation for the `#[interface]` macro. +//! +//! All `gen_*` methods live here, separate from the parsing code in `lib.rs`. Each method +//! generates one piece of the final token stream. Using `parse_quote!` (where feasible) lets +//! the compiler check well-formedness at macro-expansion time rather than silently emitting +//! syntactically invalid code. + +use super::{Guid, Interface, InterfaceMethod, InterfaceMethodArg}; +use quote::quote; + +impl Interface { + /// Generates all the code needed for a COM interface. + pub(crate) fn gen_tokens(&self, guid: &Guid) -> syn::Result { + let vis = &self.visibility; + let name = &self.name; + let docs = &self.docs; + let parent = self.parent_type(); + let vtable_name = quote::format_ident!("{}_Vtbl", name); + let guid = guid.to_tokens()?; + let implementation = self.gen_implementation(); + let com_trait = self.gen_com_trait(); + let vtable = self.gen_vtable(&vtable_name); + let conversions = self.gen_conversions(); + + Ok(quote! { + #[repr(transparent)] + #(#docs)* + #vis struct #name(#parent); + #implementation + unsafe impl ::windows_core::Interface for #name { + type Vtable = #vtable_name; + const IID: ::windows_core::GUID = #guid; + } + impl ::windows_core::RuntimeName for #name {} + impl ::core::ops::Deref for #name { + type Target = #parent; + fn deref(&self) -> &Self::Target { + &self.0 + } + } + #com_trait + #vtable + #conversions + }) + } + + /// Generates the safe caller-side methods users call on an interface pointer. + fn gen_implementation(&self) -> proc_macro2::TokenStream { + let name = &self.name; + let methods = self + .methods + .iter() + .map(|m| { + let vis = &m.visibility; + let method_name = &m.name; + + let generics = m.gen_consume_generics(); + let params = m.gen_consume_params(); + let args = m.gen_consume_args(); + let ret = &m.ret; + + if m.is_result() { + quote! { + #[inline(always)] + #vis unsafe fn #method_name<#(#generics),*>(&self, #(#params),*) #ret { + (::windows_core::Interface::vtable(self).#method_name)(::windows_core::Interface::as_raw(self), #(#args),*).ok() + } + } + } else { + quote! { + #[inline(always)] + #vis unsafe fn #method_name<#(#generics),*>(&self, #(#params),*) #ret { + (::windows_core::Interface::vtable(self).#method_name)(::windows_core::Interface::as_raw(self), #(#args),*) + } + } + } + }) + .collect::>(); + quote! { + impl #name { + #(#methods)* + } + } + } + + /// Generates the `IFoo_Impl` trait that implementors must satisfy. + fn gen_com_trait(&self) -> proc_macro2::TokenStream { + let name = quote::format_ident!("{}_Impl", self.name); + let vis = &self.visibility; + let methods = self + .methods + .iter() + .map(|m| { + let method_name = &m.name; + let docs = &m.docs; + let args = m.gen_args(); + let ret = &m.ret; + quote! { + #(#docs)* + unsafe fn #method_name(&self, #(#args),*) #ret; + } + }) + .collect::>(); + let parent = self.parent_trait_constraint(); + + quote! { + #[allow(non_camel_case_types)] + #vis trait #name: Sized + #parent { + #(#methods)* + } + } + } + + /// Generates the vtable struct and its `new` constructor. + /// + /// There are two distinct code paths: + /// + /// - **COM interface with a parent** (e.g. `IFoo: IUnknown`): The vtable starts with a + /// `base__` field of the parent's vtable type. The `new` constructor is generic over + /// `Identity: IUnknownImpl` and `const OFFSET: isize`, which are used to adjust the + /// raw `this` pointer back to the `Foo_Impl` root. + /// + /// - **Non-COM / scoped interface** (no parent): The vtable has no `base__` field and the + /// `new` constructor is simpler. A hidden `IFoo_ImplVtbl` struct is also generated + /// to hold the static vtable, together with an `IFoo::new` constructor that wraps a + /// `&T` in a `ScopedInterface`. + fn gen_vtable(&self, vtable_name: &syn::Ident) -> proc_macro2::TokenStream { + let vis = &self.visibility; + let name = &self.name; + let trait_name = quote::format_ident!("{}_Impl", name); + let implvtbl_name = quote::format_ident!("{}_ImplVtbl", name); + + let vtable_entries = self + .methods + .iter() + .map(|m| { + let method_name = &m.name; + let ret = &m.ret; + let args = m.gen_args(); + + if m.is_result() { + quote! { + pub #method_name: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, #(#args),*) -> ::windows_core::HRESULT, + } + } else { + quote! { + pub #method_name: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, #(#args),*) #ret, + } + } + }) + .collect::>(); + + let parent_vtable_generics = quote!(Identity, OFFSET); + let parent_vtable = self.parent_vtable(); + + // `or_parent_matches` is `|| ::matches(iid)` for non-IUnknown parents. + // This lets QueryInterface traverse the full interface inheritance chain. + let or_parent_matches = match parent_vtable.as_ref() { + Some(parent) if !self.parent_is_iunknown() => quote! (|| <#parent>::matches(iid)), + _ => quote!(), + }; + + let functions = self + .methods + .iter() + .map(|m| { + let method_name = &m.name; + let args = m.gen_args(); + let params = &m + .args + .iter() + .map(|a| { + let pat = &a.pat; + quote! { #pat } + }) + .collect::>(); + let ret = &m.ret; + + let ret = if m.is_result() { + quote! { -> ::windows_core::HRESULT } + } else { + quote! { #ret } + }; + + if parent_vtable.is_some() { + quote! { + unsafe extern "system" fn #method_name< + Identity: ::windows_core::IUnknownImpl, + const OFFSET: isize + >( + this: *mut ::core::ffi::c_void, // <-- This is the COM "this" pointer, which is not the same as &T or &T_Impl. + #(#args),* + ) #ret + where + Identity : #trait_name + { + // This step is essentially a virtual dispatch adjustor thunk. Its purpose is to adjust + // the "this" pointer from the address used by the COM interface to the root of the + // MyApp_Impl object. Since a given MyApp_Impl may implement more than one COM interface + // (and more than one COM interface chain), we need to know how to get from COM's "this" + // back to &MyApp_Impl. The OFFSET constant gives us the value (in pointer-sized units). + let this_outer: &Identity = &*((this as *const *const ()).offset(OFFSET) as *const Identity); + + // Last, we invoke the implementation function. + // We use explicit so that we can select the correct method + // for situations where IFoo3 derives from IFoo2 and both declare a method with + // the same name. + ::#method_name(this_outer, #(#params),*).into() + } + } + } else { + quote! { + unsafe extern "system" fn #method_name(this: *mut ::core::ffi::c_void, #(#args),*) #ret { + let this = (this as *mut *mut ::core::ffi::c_void) as *const ::windows_core::ScopedHeap; + let this = (*this).this as *const Impl; + (*this).#method_name(#(#params),*).into() + } + } + } + }) + .collect::>(); + + if let Some(parent_vtable) = parent_vtable { + let entries = self + .methods + .iter() + .map(|m| { + let method_name = &m.name; + quote!(#method_name: #method_name::) + }) + .collect::>(); + + quote! { + #[repr(C)] + #[doc(hidden)] + #vis struct #vtable_name { + pub base__: #parent_vtable, + #(#vtable_entries)* + } + impl #vtable_name { + pub const fn new< + Identity: ::windows_core::IUnknownImpl, + const OFFSET: isize, + >() -> Self + where + Identity : #trait_name + { + #(#functions)* + Self { base__: #parent_vtable::new::<#parent_vtable_generics>(), #(#entries),* } + } + + #[inline(always)] + pub fn matches(iid: &::windows_core::GUID) -> bool { + *iid == <#name as ::windows_core::Interface>::IID + #or_parent_matches + } + } + } + } else { + let entries = self + .methods + .iter() + .map(|m| { + let method_name = &m.name; + quote!(#method_name: #method_name::) + }) + .collect::>(); + + quote! { + #[repr(C)] + #[doc(hidden)] + #vis struct #vtable_name { + #(#vtable_entries)* + } + impl #vtable_name { + pub const fn new() -> Self { + #(#functions)* + Self { #(#entries),* } + } + } + struct #implvtbl_name (::core::marker::PhantomData); + impl #implvtbl_name { + const VTABLE: #vtable_name = #vtable_name::new::(); + } + impl #name { + fn new<'a, T: #trait_name>(this: &'a T) -> ::windows_core::ScopedInterface<'a, #name> { + let this = ::windows_core::ScopedHeap { vtable: &#implvtbl_name::::VTABLE as *const _ as *const _, this: this as *const _ as *const _ }; + let this = ::core::mem::ManuallyDrop::new(::windows_core::imp::Box::new(this)); + unsafe { ::windows_core::ScopedInterface::new(::core::mem::transmute(&this.vtable)) } + } + } + } + } + } + + /// Generates `Clone`, `PartialEq`, `Eq`, `Debug`, and `From` conversions. + fn gen_conversions(&self) -> proc_macro2::TokenStream { + let name = &self.name; + let name_string = format!("{name}"); + + // For COM interfaces (those with a parent), we can implement `From for IUnknown` + // safely by delegating to the parent's `From` impl, traversing the inheritance chain + // until we reach `IUnknown`. For scoped interfaces (no parent, inner field is a raw + // pointer) we fall back to a transmute since `NonNull` has no `Into`. + let into_iunknown_impl = if self.parent.is_some() { + quote! { + impl ::core::convert::From<#name> for ::windows_core::IUnknown { + fn from(value: #name) -> Self { + ::windows_core::IUnknown::from(value.0) + } + } + } + } else { + quote! { + impl ::core::convert::From<#name> for ::windows_core::IUnknown { + fn from(value: #name) -> Self { + unsafe { ::core::mem::transmute(value) } + } + } + } + }; + + quote! { + #into_iunknown_impl + impl ::core::convert::From<&#name> for ::windows_core::IUnknown { + fn from(value: &#name) -> Self { + ::core::convert::From::from(::core::clone::Clone::clone(value)) + } + } + impl ::core::clone::Clone for #name { + fn clone(&self) -> Self { + Self(self.0.clone()) + } + } + impl ::core::cmp::PartialEq for #name { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + impl ::core::cmp::Eq for #name {} + impl ::core::fmt::Debug for #name { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + f.debug_tuple(#name_string).field(&::windows_core::Interface::as_raw(self)).finish() + } + } + } + } + + /// The type token used as the inner field of the `#[repr(transparent)]` struct. + pub(crate) fn parent_type(&self) -> proc_macro2::TokenStream { + if let Some(parent) = &self.parent { + quote!(#parent) + } else { + quote!(::core::ptr::NonNull<::core::ffi::c_void>) + } + } + + fn parent_vtable(&self) -> Option { + self.parent + .as_ref() + .map(|parent| quote! { <#parent as ::windows_core::Interface>::Vtable }) + } + + fn parent_is_iunknown(&self) -> bool { + if let Some(ident) = self.parent_path().last() { + ident == "IUnknown" + } else { + false + } + } + + fn parent_path(&self) -> Vec { + if let Some(parent) = &self.parent { + parent + .segments + .iter() + .map(|segment| segment.ident.clone()) + .collect() + } else { + vec![] + } + } + + /// Returns the supertrait constraint for the `IFoo_Impl` trait. + /// + /// Returns empty tokens when the parent is `IUnknown` (to avoid a circular constraint), + /// otherwise returns `ParentName_Impl` so that implementing `IFoo_Impl` automatically + /// requires implementing all ancestor `_Impl` traits. + fn parent_trait_constraint(&self) -> proc_macro2::TokenStream { + if let Some((ident, path)) = self.parent_path().split_last() { + if ident != "IUnknown" { + let ident = quote::format_ident!("{}_Impl", ident); + return quote! { #(#path::)* #ident }; + } + } + + quote! {} + } +} + +impl InterfaceMethod { + /// Returns `true` when the method's return type is `Result` (single generic arg). + /// + /// When `true`, the generated caller-side method appends `.ok()` to convert `HRESULT` + /// to `windows_core::Result`, and the vtable entry is typed `-> HRESULT`. + pub(crate) fn is_result(&self) -> bool { + if let syn::ReturnType::Type(_, ty) = &self.ret { + if let syn::Type::Path(path) = &**ty { + if let Some(segment) = path.path.segments.last() { + let ident = segment.ident.to_string(); + if ident == "Result" { + if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { + if args.args.len() == 1 { + return true; + } + } + } + } + } + } + + false + } + + /// Generates `name: Type` argument pairs for the raw vtable function signature. + pub(crate) fn gen_args(&self) -> Vec { + self.args + .iter() + .map(|a| { + let pat = &a.pat; + let ty = &a.ty; + quote! { #pat: #ty } + }) + .collect::>() + } + + /// Generates generic parameter declarations for `Ref` / `OutRef` caller-side wrappers. + pub(crate) fn gen_consume_generics(&self) -> Vec { + self.args + .iter() + .enumerate() + .filter_map(|(generic_index, a)| { + if let Some((ty, ident)) = a.borrow_type() { + let generic_ident = quote::format_ident!("P{generic_index}"); + if ident == "Ref" { + Some(quote! { #generic_ident: ::windows_core::Param<#ty> }) + } else { + Some(quote! { #generic_ident: ::windows_core::OutParam<#ty> }) + } + } else { + None + } + }) + .collect::>() + } + + /// Generates parameter declarations for the caller-side method, replacing `Ref` / + /// `OutRef` arguments with their corresponding generic parameter types. + pub(crate) fn gen_consume_params(&self) -> Vec { + self.args + .iter() + .enumerate() + .map(|(generic_index, a)| { + let pat = &a.pat; + + if a.borrow_type().is_some() { + let generic_ident = quote::format_ident!("P{generic_index}"); + quote! { #pat: #generic_ident } + } else { + let ty = &a.ty; + quote! { #pat: #ty } + } + }) + .collect::>() + } + + /// Generates the argument expressions forwarded to the raw vtable function from the + /// caller-side method. + pub(crate) fn gen_consume_args(&self) -> Vec { + self.args + .iter() + .map(|a| { + let pat = &a.pat; + + if let Some((_, ident)) = a.borrow_type() { + if ident == "Ref" { + quote! { #pat.param().borrow() } + } else { + quote! { #pat.borrow_mut() } + } + } else { + quote! { #pat } + } + }) + .collect::>() + } +} + +impl InterfaceMethodArg { + /// If this argument is `Ref` or `OutRef`, returns `(T, "Ref")` or `(T, "OutRef")`. + pub(crate) fn borrow_type(&self) -> Option<(syn::Type, String)> { + if let syn::Type::Path(path) = &*self.ty { + if let Some(segment) = path.path.segments.last() { + let ident = segment.ident.to_string(); + if matches!(ident.as_str(), "Ref" | "OutRef") { + if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { + if args.args.len() == 1 { + if let Some(syn::GenericArgument::Type(ty)) = args.args.first() { + return Some((ty.clone(), ident)); + } + } + } + } + } + } + + None + } +} diff --git a/third_party/windows-winui/windows-interface/src/guid.rs b/third_party/windows-winui/windows-interface/src/guid.rs new file mode 100644 index 0000000000..d3f570fefc --- /dev/null +++ b/third_party/windows-winui/windows-interface/src/guid.rs @@ -0,0 +1,101 @@ +//! GUID parsing and token generation for the `#[interface]` macro attribute. + +use quote::quote; + +/// The parsed GUID attribute on an `#[interface]` invocation. +/// +/// ```rust,ignore +/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] +/// //^ this part is parsed as a Guid +/// unsafe trait IUIAnimationVariable: IUnknown { ... } +/// ``` +/// +/// When the attribute is absent (i.e. `#[interface]` with no argument), the GUID is `None` +/// and `to_tokens` emits `GUID::zeroed()`. +pub(crate) struct Guid(pub(crate) Option); + +impl Guid { + /// Converts the parsed GUID string into a `::windows_core::GUID { ... }` token stream. + pub(crate) fn to_tokens(&self) -> syn::Result { + fn hex_lit(num: &str) -> syn::LitInt { + syn::LitInt::new(&format!("0x{num}"), proc_macro2::Span::call_site()) + } + + fn ensure_length( + part: Option<&str>, + index: usize, + length: usize, + span: proc_macro2::Span, + ) -> syn::Result { + let Some(part) = part else { + return Err(syn::Error::new( + span, + format!("IID part at index {index} is missing"), + )); + }; + + if part.len() != length { + return Err(syn::Error::new( + span, + format!( + "The IID part at index {} must be {} characters long but was {} characters", + index, + length, + part.len() + ), + )); + } + + Ok(part.to_owned()) + } + + if let Some(value) = &self.0 { + let guid_value = value.value(); + let mut delimited = guid_value.split('-').fuse(); + let chunks = [ + ensure_length(delimited.next(), 0, 8, value.span())?, + ensure_length(delimited.next(), 1, 4, value.span())?, + ensure_length(delimited.next(), 2, 4, value.span())?, + ensure_length(delimited.next(), 3, 4, value.span())?, + ensure_length(delimited.next(), 4, 12, value.span())?, + ]; + + let data1 = hex_lit(&chunks[0]); + let data2 = hex_lit(&chunks[1]); + let data3 = hex_lit(&chunks[2]); + let (data4_1, data4_2) = chunks[3].split_at(2); + let data4_1 = hex_lit(data4_1); + let data4_2 = hex_lit(data4_2); + let (data4_3, rest) = chunks[4].split_at(2); + let data4_3 = hex_lit(data4_3); + let (data4_4, rest) = rest.split_at(2); + let data4_4 = hex_lit(data4_4); + let (data4_5, rest) = rest.split_at(2); + let data4_5 = hex_lit(data4_5); + let (data4_6, rest) = rest.split_at(2); + let data4_6 = hex_lit(data4_6); + let (data4_7, data4_8) = rest.split_at(2); + let data4_7 = hex_lit(data4_7); + let data4_8 = hex_lit(data4_8); + Ok(quote! { + ::windows_core::GUID { + data1: #data1, + data2: #data2, + data3: #data3, + data4: [#data4_1, #data4_2, #data4_3, #data4_4, #data4_5, #data4_6, #data4_7, #data4_8] + } + }) + } else { + Ok(quote! { + ::windows_core::GUID::zeroed() + }) + } + } +} + +impl syn::parse::Parse for Guid { + fn parse(cursor: syn::parse::ParseStream) -> syn::Result { + let string: Option = cursor.parse().ok(); + Ok(Self(string)) + } +} diff --git a/third_party/windows-winui/windows-interface/src/lib.rs b/third_party/windows-winui/windows-interface/src/lib.rs new file mode 100644 index 0000000000..b7e633d958 --- /dev/null +++ b/third_party/windows-winui/windows-interface/src/lib.rs @@ -0,0 +1,248 @@ +//! Define COM interfaces to call or implement. +//! +//! Take a look at [macro@interface] for an example. +//! +//! Learn more about Rust for Windows here: +//! +//! # Architecture +//! +//! ## What this macro generates +//! +//! For an interface declared as: +//! +//! ```rust,ignore +//! #[interface("094d70d6-5202-44b8-abb8-43860da5aca2")] +//! unsafe trait IFoo: IUnknown { +//! fn GetValue(&self, value: *mut i32) -> HRESULT; +//! } +//! ``` +//! +//! The macro emits: +//! +//! - `struct IFoo(IUnknown)` — a `#[repr(transparent)]` struct wrapping the parent. +//! - `unsafe impl Interface for IFoo` — with the IID constant. +//! - `impl Deref for IFoo` — to reach parent-interface methods. +//! - `impl IFoo { fn GetValue(...) }` — safe wrapper that calls through the vtable. +//! - `trait IFoo_Impl: Sized` — the trait that `#[implement]` users must implement on +//! their `Foo_Impl` type. +//! - `struct IFoo_Vtbl` — the vtable layout, with a `new::()` constructor +//! and a `matches(iid)` helper used by `QueryInterface`. +//! - `Clone`, `PartialEq`, `Eq`, `Debug`, and `From for IUnknown` implementations. +//! +//! ## Naming conventions +//! +//! - `IFoo` — the interface struct, usable as a COM pointer. +//! - `IFoo_Vtbl` — the vtable struct (hidden from docs). +//! - `IFoo_Impl` — the implementation trait; see `windows-implement` for who implements it. +//! +//! ## Relationship to `windows-implement` +//! +//! `#[interface]` and `#[implement]` (in `windows-implement`) must agree on: +//! - The `_Impl` naming convention. +//! - The vtable `new::()` constructor signature. +//! - The `matches(iid)` helper used during `QueryInterface`. + +use syn::spanned::Spanned; + +mod gen; +mod guid; +pub(crate) use guid::Guid; + +#[cfg(test)] +mod tests; + +/// Defines a COM interface to call or implement. +/// +/// # Example +/// ```rust,no_run +/// use windows_core::*; +/// +/// #[interface("094d70d6-5202-44b8-abb8-43860da5aca2")] +/// unsafe trait IValue: IUnknown { +/// fn GetValue(&self, value: *mut i32) -> HRESULT; +/// } +/// +/// #[implement(IValue)] +/// struct Value(i32); +/// +/// impl IValue_Impl for Value_Impl { +/// unsafe fn GetValue(&self, value: *mut i32) -> HRESULT { +/// *value = self.0; +/// HRESULT(0) +/// } +/// } +/// +/// let object: IValue = Value(123).into(); +/// // Call interface methods... +/// ``` +#[proc_macro_attribute] +pub fn interface( + attributes: proc_macro::TokenStream, + original_type: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + interface_core(attributes.into(), original_type.into()).into() +} + +fn interface_core( + attributes: proc_macro2::TokenStream, + item_tokens: proc_macro2::TokenStream, +) -> proc_macro2::TokenStream { + let guid = match syn::parse2::(attributes) { + Ok(g) => g, + Err(e) => return e.into_compile_error(), + }; + let interface = match syn::parse2::(item_tokens) { + Ok(i) => i, + Err(e) => return e.into_compile_error(), + }; + match interface.gen_tokens(&guid) { + Ok(t) => t, + Err(e) => e.into_compile_error(), + } +} + +/// A parsed `#[interface]` trait definition. +/// +/// ```rust,ignore +/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] +/// unsafe trait IUIAnimationVariable: IUnknown { +/// //^ this is parsed as an Interface +/// fn GetValue(&self, value: *mut f64) -> HRESULT; +/// } +/// ``` +pub(crate) struct Interface { + pub(crate) visibility: syn::Visibility, + pub(crate) name: syn::Ident, + pub(crate) parent: Option, + pub(crate) methods: Vec, + pub(crate) docs: Vec, +} + +impl syn::parse::Parse for Interface { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let attributes = input.call(syn::Attribute::parse_outer)?; + let mut docs = Vec::new(); + for attr in attributes.into_iter() { + let path = attr.path(); + if path.is_ident("doc") { + docs.push(attr); + } else { + return Err(syn::Error::new(path.span(), "Unrecognized attribute")); + } + } + + let visibility = input.parse::()?; + _ = input.parse::()?; + _ = input.parse::()?; + let name = input.parse::()?; + _ = input.parse::(); + let parent = input.parse::().ok(); + let content; + syn::braced!(content in input); + let mut methods = Vec::new(); + while !content.is_empty() { + methods.push(content.parse::()?); + } + Ok(Self { + visibility, + methods, + name, + parent, + docs, + }) + } +} + +/// A single method declaration inside an `#[interface]` trait body. +/// +/// ```rust,ignore +/// #[windows_interface::interface("8CEEB155-2849-4ce5-9448-91FF70E1E4D9")] +/// unsafe trait IUIAnimationVariable: IUnknown { +/// fn GetValue(&self, value: *mut f64) -> HRESULT; +/// //^ this is parsed as an InterfaceMethod +/// } +/// ``` +pub(crate) struct InterfaceMethod { + pub(crate) name: syn::Ident, + pub(crate) visibility: syn::Visibility, + pub(crate) args: Vec, + pub(crate) ret: syn::ReturnType, + pub(crate) docs: Vec, +} + +impl syn::parse::Parse for InterfaceMethod { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let docs = input.call(syn::Attribute::parse_outer)?; + let visibility = input.parse::()?; + let method = input.parse::()?; + + // Reject non-doc attributes. + if let Some(i) = docs.iter().find(|a| !a.path().is_ident("doc")) { + return Err(syn::Error::new(i.span(), "unexpected attribute")); + } + // Reject default method bodies. + if let Some(i) = &method.default { + return Err(syn::Error::new( + i.span(), + "unexpected default method implementation", + )); + } + + let sig = method.sig; + + // Reject unsupported function-signature features. + if let Some(i) = &sig.abi { + return Err(syn::Error::new(i.span(), "unexpected abi declaration")); + } + if let Some(i) = &sig.asyncness { + return Err(syn::Error::new(i.span(), "unexpected async declaration")); + } + if let Some(i) = sig.generics.params.iter().next() { + return Err(syn::Error::new(i.span(), "unexpected generics declaration")); + } + if let Some(i) = &sig.constness { + return Err(syn::Error::new(i.span(), "unexpected const declaration")); + } + if sig.receiver().is_none() { + return Err(syn::Error::new( + sig.span(), + "expected the method to have &self as its first argument", + )); + } + if let Some(i) = &sig.variadic { + return Err(syn::Error::new(i.span(), "unexpected variadic args")); + } + + let args = sig + .inputs + .into_iter() + .filter_map(|a| match a { + syn::FnArg::Receiver(_) => None, + syn::FnArg::Typed(p) => Some(p), + }) + .map(|p| { + Ok(InterfaceMethodArg { + ty: p.ty, + pat: p.pat, + }) + }) + .collect::, syn::Error>>()?; + + let ret = sig.output; + Ok(Self { + name: sig.ident, + visibility, + args, + ret, + docs, + }) + } +} + +/// A single argument in an [`InterfaceMethod`]. +pub(crate) struct InterfaceMethodArg { + /// The type of the argument. + pub(crate) ty: Box, + /// The pattern (name) of the argument. + pub(crate) pat: Box, +} diff --git a/third_party/windows-winui/windows-interface/src/tests.rs b/third_party/windows-winui/windows-interface/src/tests.rs new file mode 100644 index 0000000000..30ef1d1377 --- /dev/null +++ b/third_party/windows-winui/windows-interface/src/tests.rs @@ -0,0 +1,233 @@ +//! Tests for the `#[interface]` macro that verify the generated code structure. +//! +//! These tests call `interface_core` directly and check that the formatted output contains +//! the expected declarations, vtable, and trait definitions. Any change to the code generator +//! that silently removes or renames a key item will be caught as a test failure. +//! +//! To inspect the full formatted output of a test, run with `--nocapture`: +//! +//! ```text +//! cargo test -p windows-interface --lib -- --nocapture --test-threads=1 +//! ``` + +use std::io::{Read, Write}; +use std::process::{Command, Stdio}; + +use proc_macro2::TokenStream; +use quote::quote; + +fn interface(attributes: TokenStream, item_tokens: TokenStream) -> String { + let out_tokens = crate::interface_core(attributes, item_tokens); + let tokens_string = out_tokens.to_string(); + + let out_string = rustfmt(&tokens_string); + println!("// output of #[interface] :"); + println!(); + println!("{out_string}"); + out_string +} + +fn rustfmt(input: &str) -> String { + let mut rustfmt = Command::new("rustfmt"); + + rustfmt.stdin(Stdio::piped()); + rustfmt.stdout(Stdio::piped()); + rustfmt.stderr(Stdio::inherit()); + + let mut child = match rustfmt.spawn() { + Ok(c) => c, + Err(e) => { + eprintln!("failed to spawn rustfmt: {e:?}"); + return input.to_string(); + } + }; + + let mut stdout = child.stdout.take().unwrap(); + + // spawn thread to read stdout + let stdout_thread = std::thread::spawn(move || { + let mut buf = String::new(); + stdout.read_to_string(&mut buf).unwrap(); + buf + }); + + // write unformatted into stdin + let mut stdin = child.stdin.take().unwrap(); + stdin.write_all(input.as_bytes()).unwrap(); + drop(stdin); + + let stdout_string: String = stdout_thread.join().unwrap(); + + let exit = child.wait().unwrap(); + if !exit.success() { + eprintln!("rustfmt terminated with failure status code"); + return input.to_string(); + } + + stdout_string +} + +fn check(output: &str, expected_items: &[&str]) { + for item in expected_items { + assert!( + output.contains(item), + "output does not contain expected item:\n expected: {item}\n\nfull output:\n{output}" + ); + } +} + +#[test] +fn com_interface_with_parent() { + let output = interface( + quote!("094d70d6-5202-44b8-abb8-43860da5aca2"), + quote! { + unsafe trait IFoo: IUnknown { + fn GetValue(&self, value: *mut i32) -> HRESULT; + } + }, + ); + check( + &output, + &[ + "struct IFoo", + "unsafe impl ::windows_core::Interface for IFoo", + "impl ::windows_core::RuntimeName for IFoo", + "impl ::core::ops::Deref for IFoo", + "trait IFoo_Impl", + "unsafe fn GetValue", + "struct IFoo_Vtbl", + "pub GetValue:", + "impl IFoo_Vtbl", + "pub const fn new<", + "pub fn matches", + "impl ::core::convert::From for ::windows_core::IUnknown", + "impl ::core::clone::Clone for IFoo", + "impl ::core::fmt::Debug for IFoo", + ], + ); + // The IID should contain the parsed GUID values. + assert!( + output.contains("0x094d70d6"), + "output does not contain expected GUID data1" + ); +} + +#[test] +fn com_interface_inheriting_parent() { + let output = interface( + quote!("00000000-0000-0000-0000-000000000002"), + quote! { + unsafe trait IBar: IFoo { + fn SetValue(&self, value: i32) -> HRESULT; + } + }, + ); + check( + &output, + &[ + "struct IBar", + "unsafe impl ::windows_core::Interface for IBar", + "impl ::core::ops::Deref for IBar", + // IBar_Impl must require IFoo_Impl as a supertrait. + "trait IBar_Impl", + "IFoo_Impl", + "struct IBar_Vtbl", + // base__ comes from parent vtable. + "pub base__:", + "pub SetValue:", + "pub fn matches", + // matches must chain to parent. + "|| <", + ">::matches(iid)", + ], + ); +} + +#[test] +fn non_com_interface() { + // Non-COM interface: no parent (no IUnknown base). + let output = interface( + quote!(), + quote! { + unsafe trait ICallback { + fn Invoke(&self, result: i32); + } + }, + ); + check( + &output, + &[ + "struct ICallback", + "unsafe impl ::windows_core::Interface for ICallback", + "trait ICallback_Impl", + "unsafe fn Invoke", + "struct ICallback_Vtbl", + "pub Invoke:", + // Non-COM path uses simpler Impl-generic constructor. + "pub const fn new", + // Non-COM interfaces also generate the ImplVtbl helper. + "struct ICallback_ImplVtbl", + // And a Foo::new constructor for ScopedInterface. + "fn new<'a, T: ICallback_Impl>", + ], + ); + // Non-COM interface should NOT emit a `matches` function (no QueryInterface). + assert!( + !output.contains("pub fn matches"), + "non_com_interface output unexpectedly contains `matches`" + ); +} + +#[test] +fn interface_no_guid() { + // Omitting the GUID should produce GUID::zeroed(). + let output = interface( + quote!(), + quote! { + unsafe trait INoGuid: IUnknown {} + }, + ); + check( + &output, + &[ + "struct INoGuid", + "::windows_core::GUID::zeroed()", + "trait INoGuid_Impl", + ], + ); +} + +#[test] +fn interface_with_result_return() { + // Methods returning `Result` should get the `.ok()` wrapper and HRESULT vtable entry. + let output = interface( + quote!("00000000-0000-0000-0000-000000000003"), + quote! { + unsafe trait IReader: IUnknown { + fn Read(&self, buf: *mut u8, len: u32) -> Result; + } + }, + ); + check( + &output, + &[ + "fn Read", + // Caller side wraps with .ok() + ".ok()", + // Vtable entry must use HRESULT. + "-> ::windows_core::HRESULT", + ], + ); +} + +#[test] +fn public_visibility() { + let output = interface( + quote!("00000000-0000-0000-0000-000000000004"), + quote! { + pub unsafe trait IPublic: IUnknown {} + }, + ); + // Visibility must be propagated to the struct and trait. + check(&output, &["pub struct IPublic", "pub trait IPublic_Impl"]); +} diff --git a/third_party/windows-winui/windows-link/Cargo.toml b/third_party/windows-winui/windows-link/Cargo.toml new file mode 100644 index 0000000000..cb6c02f662 --- /dev/null +++ b/third_party/windows-winui/windows-link/Cargo.toml @@ -0,0 +1,11 @@ + +[package] +name = "windows-link" +version = "0.2.1" +edition = "2021" +rust-version = "1.71" +license = "MIT OR Apache-2.0" +description = "Linking for Windows" +repository = "https://github.com/microsoft/windows-rs" +categories = ["os::windows-apis"] +readme = "readme.md" diff --git a/third_party/windows-winui/windows-link/license-apache-2.0 b/third_party/windows-winui/windows-link/license-apache-2.0 new file mode 100644 index 0000000000..b5ed4ecec2 --- /dev/null +++ b/third_party/windows-winui/windows-link/license-apache-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/windows-winui/windows-link/license-mit b/third_party/windows-winui/windows-link/license-mit new file mode 100644 index 0000000000..9e841e7a26 --- /dev/null +++ b/third_party/windows-winui/windows-link/license-mit @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/third_party/windows-winui/windows-link/readme.md b/third_party/windows-winui/windows-link/readme.md new file mode 100644 index 0000000000..668c5eb5b2 --- /dev/null +++ b/third_party/windows-winui/windows-link/readme.md @@ -0,0 +1,37 @@ +## Linking for Windows + +The [windows-link](https://crates.io/crates/windows-link) crate provides the `link` macro that simplifies linking. The `link` macro is much the same as the one provided by [windows-targets](https://crates.io/crates/windows-targets) but uses `raw-dylib` and thus does not require import lib files. + +* [Getting started](https://kennykerr.ca/rust-getting-started/) +* [Samples](https://github.com/microsoft/windows-rs/tree/master/crates/samples) +* [Releases](https://github.com/microsoft/windows-rs/releases) + +Start by adding the following to your Cargo.toml file: + +```toml +[dependencies.windows-link] +version = "0.2" +``` + +Use the `link` macro to define the external functions you wish to call: + +```rust,ignore +windows_link::link!("kernel32.dll" "system" fn SetLastError(code: u32)); +windows_link::link!("kernel32.dll" "system" fn GetLastError() -> u32); + +unsafe { + SetLastError(1234); + assert_eq!(GetLastError(), 1234); +} +``` + +In addition to declaring the function, the macro also emits a `pub type` +alias of the same name describing the function's signature. This is handy +when you need to store or pass around the function pointer (for example, +after resolving the symbol at runtime via `GetProcAddress`): + +```rust,ignore +windows_link::link!("kernel32.dll" "system" fn GetTickCount() -> u32); +// `GetTickCount` is also available as a function-pointer type: +let f: GetTickCount = GetTickCount; +``` diff --git a/third_party/windows-winui/windows-link/src/lib.rs b/third_party/windows-winui/windows-link/src/lib.rs new file mode 100644 index 0000000000..f3cdab7e28 --- /dev/null +++ b/third_party/windows-winui/windows-link/src/lib.rs @@ -0,0 +1,63 @@ +#![doc = include_str!("../readme.md")] +#![no_std] + +/// Defines an external function to import. +/// +/// Expands to an `extern` block declaring the function as well as a `pub type` +/// alias matching the function's signature. The type alias has the same name +/// as the function, lives in the type namespace, and is useful for storing or +/// passing around the function pointer (for example, after resolving the +/// symbol at runtime via `GetProcAddress`). +#[cfg(all(windows, target_arch = "x86"))] +#[macro_export] +macro_rules! link { + ($library:literal $abi:literal $($link_name:literal)? fn $name:ident($($params:tt)*) $(-> $ret:ty)?) => ( + #[link(name = $library, kind = "raw-dylib", modifiers = "+verbatim", import_name_type = "undecorated")] + extern $abi { + $(#[link_name=$link_name])? + pub fn $name($($params)*) $(-> $ret)?; + } + #[allow(non_camel_case_types)] + pub type $name = unsafe extern $abi fn($($params)*) $(-> $ret)?; + ) +} + +/// Defines an external function to import. +/// +/// Expands to an `extern` block declaring the function as well as a `pub type` +/// alias matching the function's signature. The type alias has the same name +/// as the function, lives in the type namespace, and is useful for storing or +/// passing around the function pointer (for example, after resolving the +/// symbol at runtime via `GetProcAddress`). +#[cfg(all(windows, not(target_arch = "x86")))] +#[macro_export] +macro_rules! link { + ($library:literal $abi:literal $($link_name:literal)? fn $name:ident($($params:tt)*) $(-> $ret:ty)?) => ( + #[link(name = $library, kind = "raw-dylib", modifiers = "+verbatim")] + extern $abi { + $(#[link_name=$link_name])? + pub fn $name($($params)*) $(-> $ret)?; + } + #[allow(non_camel_case_types)] + pub type $name = unsafe extern $abi fn($($params)*) $(-> $ret)?; + ) +} + +/// Defines an external function to import. +/// +/// Expands to an `extern` block declaring the function as well as a `pub type` +/// alias matching the function's signature. The type alias has the same name +/// as the function, lives in the type namespace, and is useful for storing or +/// passing around the function pointer (for example, after resolving the +/// symbol at runtime via `GetProcAddress`). +#[cfg(not(windows))] +#[macro_export] +macro_rules! link { + ($library:literal $abi:literal $($link_name:literal)? fn $name:ident($($params:tt)*) $(-> $ret:ty)?) => ( + extern $abi { + pub fn $name($($params)*) $(-> $ret)?; + } + #[allow(non_camel_case_types)] + pub type $name = unsafe extern $abi fn($($params)*) $(-> $ret)?; + ) +} diff --git a/third_party/windows-winui/windows-numerics/Cargo.toml b/third_party/windows-winui/windows-numerics/Cargo.toml new file mode 100644 index 0000000000..4523215a71 --- /dev/null +++ b/third_party/windows-winui/windows-numerics/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "windows-numerics" +version = "0.3.1" +edition = "2021" +rust-version = "1.82" +license = "MIT OR Apache-2.0" +description = "Windows numeric types" +repository = "https://github.com/microsoft/windows-rs" +categories = ["os::windows-apis"] +readme = "readme.md" + +[dependencies] +windows-core = { path = "../windows-core" } +windows-link = { path = "../windows-link" } + +[features] +default = ["std"] +std = ["windows-core/std"] + +[package.metadata.docs.rs] +targets = [] diff --git a/third_party/windows-winui/windows-numerics/license-apache-2.0 b/third_party/windows-winui/windows-numerics/license-apache-2.0 new file mode 100644 index 0000000000..b5ed4ecec2 --- /dev/null +++ b/third_party/windows-winui/windows-numerics/license-apache-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/windows-winui/windows-numerics/license-mit b/third_party/windows-winui/windows-numerics/license-mit new file mode 100644 index 0000000000..9e841e7a26 --- /dev/null +++ b/third_party/windows-winui/windows-numerics/license-mit @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/third_party/windows-winui/windows-numerics/readme.md b/third_party/windows-winui/windows-numerics/readme.md new file mode 100644 index 0000000000..b9050c05b8 --- /dev/null +++ b/third_party/windows-winui/windows-numerics/readme.md @@ -0,0 +1,7 @@ +## Windows numeric types + +The [windows-numerics](https://crates.io/crates/windows-numerics) crate provides graphics-oriented math types for Windows. + +* [Getting started](https://kennykerr.ca/rust-getting-started/) +* [Samples](https://github.com/microsoft/windows-rs/tree/master/crates/samples) +* [Releases](https://github.com/microsoft/windows-rs/releases) diff --git a/third_party/windows-winui/windows-numerics/src/bindings.rs b/third_party/windows-winui/windows-numerics/src/bindings.rs new file mode 100644 index 0000000000..b929951204 --- /dev/null +++ b/third_party/windows-winui/windows-numerics/src/bindings.rs @@ -0,0 +1,99 @@ +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Matrix3x2 { + pub M11: f32, + pub M12: f32, + pub M21: f32, + pub M22: f32, + pub M31: f32, + pub M32: f32, +} +impl windows_core::TypeKind for Matrix3x2 { + type TypeKind = windows_core::CopyType; +} +impl windows_core::RuntimeType for Matrix3x2 { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::from_slice( + b"struct(Windows.Foundation.Numerics.Matrix3x2;f4;f4;f4;f4;f4;f4)", + ); + const NAME: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::from_slice(b"Windows.Foundation.Numerics.Matrix3x2"); +} +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Matrix4x4 { + pub M11: f32, + pub M12: f32, + pub M13: f32, + pub M14: f32, + pub M21: f32, + pub M22: f32, + pub M23: f32, + pub M24: f32, + pub M31: f32, + pub M32: f32, + pub M33: f32, + pub M34: f32, + pub M41: f32, + pub M42: f32, + pub M43: f32, + pub M44: f32, +} +impl windows_core::TypeKind for Matrix4x4 { + type TypeKind = windows_core::CopyType; +} +impl windows_core::RuntimeType for Matrix4x4 { + const SIGNATURE : windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::from_slice (b"struct(Windows.Foundation.Numerics.Matrix4x4;f4;f4;f4;f4;f4;f4;f4;f4;f4;f4;f4;f4;f4;f4;f4;f4)") ; + const NAME: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::from_slice(b"Windows.Foundation.Numerics.Matrix4x4"); +} +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Vector2 { + pub X: f32, + pub Y: f32, +} +impl windows_core::TypeKind for Vector2 { + type TypeKind = windows_core::CopyType; +} +impl windows_core::RuntimeType for Vector2 { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::from_slice( + b"struct(Windows.Foundation.Numerics.Vector2;f4;f4)", + ); + const NAME: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::from_slice(b"Windows.Foundation.Numerics.Vector2"); +} +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Vector3 { + pub X: f32, + pub Y: f32, + pub Z: f32, +} +impl windows_core::TypeKind for Vector3 { + type TypeKind = windows_core::CopyType; +} +impl windows_core::RuntimeType for Vector3 { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::from_slice( + b"struct(Windows.Foundation.Numerics.Vector3;f4;f4;f4)", + ); + const NAME: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::from_slice(b"Windows.Foundation.Numerics.Vector3"); +} +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Vector4 { + pub X: f32, + pub Y: f32, + pub Z: f32, + pub W: f32, +} +impl windows_core::TypeKind for Vector4 { + type TypeKind = windows_core::CopyType; +} +impl windows_core::RuntimeType for Vector4 { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::from_slice( + b"struct(Windows.Foundation.Numerics.Vector4;f4;f4;f4;f4)", + ); + const NAME: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::from_slice(b"Windows.Foundation.Numerics.Vector4"); +} diff --git a/third_party/windows-winui/windows-numerics/src/lib.rs b/third_party/windows-winui/windows-numerics/src/lib.rs new file mode 100644 index 0000000000..8fb7ff7ead --- /dev/null +++ b/third_party/windows-winui/windows-numerics/src/lib.rs @@ -0,0 +1,13 @@ +#![expect(missing_docs, non_snake_case)] +#![doc = include_str!("../readme.md")] +#![cfg_attr(all(not(feature = "std")), no_std)] +#![forbid(unsafe_code)] + +mod bindings; +pub use bindings::*; + +mod matrix3x2; +mod matrix4x4; +mod vector2; +mod vector3; +mod vector4; diff --git a/third_party/windows-winui/windows-numerics/src/matrix3x2.rs b/third_party/windows-winui/windows-numerics/src/matrix3x2.rs new file mode 100644 index 0000000000..1237d2375a --- /dev/null +++ b/third_party/windows-winui/windows-numerics/src/matrix3x2.rs @@ -0,0 +1,195 @@ +use super::*; + +impl Matrix3x2 { + pub const fn identity() -> Self { + Self { + M11: 1.0, + M12: 0.0, + M21: 0.0, + M22: 1.0, + M31: 0.0, + M32: 0.0, + } + } + pub const fn translation(x: f32, y: f32) -> Self { + Self { + M11: 1.0, + M12: 0.0, + M21: 0.0, + M22: 1.0, + M31: x, + M32: y, + } + } + #[cfg(feature = "std")] + pub fn rotation(angle: f32) -> Self { + Self::rotation_around(angle, Vector2::zero()) + } + #[cfg(feature = "std")] + pub fn rotation_around(angle: f32, center: Vector2) -> Self { + let (sin, cos) = angle.to_radians().sin_cos(); + Self { + M11: cos, + M12: sin, + M21: -sin, + M22: cos, + M31: center.X * (1.0 - cos) + center.Y * sin, + M32: center.Y * (1.0 - cos) - center.X * sin, + } + } + pub fn scale(scale_x: f32, scale_y: f32) -> Self { + Self::scale_around(scale_x, scale_y, Vector2::zero()) + } + pub fn scale_around(scale_x: f32, scale_y: f32, center: Vector2) -> Self { + Self { + M11: scale_x, + M12: 0.0, + M21: 0.0, + M22: scale_y, + M31: center.X - scale_x * center.X, + M32: center.Y - scale_y * center.Y, + } + } + #[cfg(feature = "std")] + pub fn skew(angle_x: f32, angle_y: f32) -> Self { + Self::skew_around(angle_x, angle_y, Vector2::zero()) + } + #[cfg(feature = "std")] + pub fn skew_around(angle_x: f32, angle_y: f32, center: Vector2) -> Self { + let tan_x = angle_x.to_radians().tan(); + let tan_y = angle_y.to_radians().tan(); + Self { + M11: 1.0, + M12: tan_y, + M21: tan_x, + M22: 1.0, + M31: -center.Y * tan_x, + M32: -center.X * tan_y, + } + } + fn impl_add(&self, rhs: &Self) -> Self { + Self { + M11: self.M11 + rhs.M11, + M12: self.M12 + rhs.M12, + M21: self.M21 + rhs.M21, + M22: self.M22 + rhs.M22, + M31: self.M31 + rhs.M31, + M32: self.M32 + rhs.M32, + } + } + fn impl_sub(&self, rhs: &Self) -> Self { + Self { + M11: self.M11 - rhs.M11, + M12: self.M12 - rhs.M12, + M21: self.M21 - rhs.M21, + M22: self.M22 - rhs.M22, + M31: self.M31 - rhs.M31, + M32: self.M32 - rhs.M32, + } + } + fn impl_mul(&self, rhs: &Self) -> Self { + Self { + M11: self.M11 * rhs.M11 + self.M12 * rhs.M21, + M12: self.M11 * rhs.M12 + self.M12 * rhs.M22, + M21: self.M21 * rhs.M11 + self.M22 * rhs.M21, + M22: self.M21 * rhs.M12 + self.M22 * rhs.M22, + M31: self.M31 * rhs.M11 + self.M32 * rhs.M21 + rhs.M31, + M32: self.M31 * rhs.M12 + self.M32 * rhs.M22 + rhs.M32, + } + } + fn impl_mul_f32(&self, rhs: f32) -> Self { + Self { + M11: self.M11 * rhs, + M12: self.M12 * rhs, + M21: self.M21 * rhs, + M22: self.M22 * rhs, + M31: self.M31 * rhs, + M32: self.M32 * rhs, + } + } +} + +impl core::ops::Add for Matrix3x2 { + type Output = Self; + fn add(self, rhs: Self) -> Self { + self.impl_add(&rhs) + } +} +impl core::ops::Add<&Self> for Matrix3x2 { + type Output = Self; + fn add(self, rhs: &Self) -> Self { + self.impl_add(rhs) + } +} +impl core::ops::Add for &Matrix3x2 { + type Output = Matrix3x2; + fn add(self, rhs: Matrix3x2) -> Matrix3x2 { + self.impl_add(&rhs) + } +} +impl core::ops::Add<&Matrix3x2> for &Matrix3x2 { + type Output = Matrix3x2; + fn add(self, rhs: &Matrix3x2) -> Matrix3x2 { + self.impl_add(rhs) + } +} +impl core::ops::Sub for Matrix3x2 { + type Output = Self; + fn sub(self, rhs: Self) -> Self { + self.impl_sub(&rhs) + } +} +impl core::ops::Sub<&Self> for Matrix3x2 { + type Output = Self; + fn sub(self, rhs: &Self) -> Self { + self.impl_sub(rhs) + } +} +impl core::ops::Sub for &Matrix3x2 { + type Output = Matrix3x2; + fn sub(self, rhs: Matrix3x2) -> Matrix3x2 { + self.impl_sub(&rhs) + } +} +impl core::ops::Sub<&Matrix3x2> for &Matrix3x2 { + type Output = Matrix3x2; + fn sub(self, rhs: &Matrix3x2) -> Matrix3x2 { + self.impl_sub(rhs) + } +} +impl core::ops::Mul for Matrix3x2 { + type Output = Self; + fn mul(self, rhs: Self) -> Self { + self.impl_mul(&rhs) + } +} +impl core::ops::Mul<&Self> for Matrix3x2 { + type Output = Self; + fn mul(self, rhs: &Self) -> Self { + self.impl_mul(rhs) + } +} +impl core::ops::Mul for &Matrix3x2 { + type Output = Matrix3x2; + fn mul(self, rhs: Matrix3x2) -> Matrix3x2 { + self.impl_mul(&rhs) + } +} +impl core::ops::Mul<&Matrix3x2> for &Matrix3x2 { + type Output = Matrix3x2; + fn mul(self, rhs: &Matrix3x2) -> Matrix3x2 { + self.impl_mul(rhs) + } +} +impl core::ops::Mul for Matrix3x2 { + type Output = Self; + fn mul(self, rhs: f32) -> Self { + self.impl_mul_f32(rhs) + } +} +impl core::ops::Mul for &Matrix3x2 { + type Output = Matrix3x2; + fn mul(self, rhs: f32) -> Matrix3x2 { + self.impl_mul_f32(rhs) + } +} diff --git a/third_party/windows-winui/windows-numerics/src/matrix4x4.rs b/third_party/windows-winui/windows-numerics/src/matrix4x4.rs new file mode 100644 index 0000000000..ce72fa2d0a --- /dev/null +++ b/third_party/windows-winui/windows-numerics/src/matrix4x4.rs @@ -0,0 +1,232 @@ +use super::*; + +impl Matrix4x4 { + pub const fn translation(x: f32, y: f32, z: f32) -> Self { + Self { + M11: 1.0, + M12: 0.0, + M13: 0.0, + M14: 0.0, + M21: 0.0, + M22: 1.0, + M23: 0.0, + M24: 0.0, + M31: 0.0, + M32: 0.0, + M33: 1.0, + M34: 0.0, + M41: x, + M42: y, + M43: z, + M44: 1.0, + } + } + #[cfg(feature = "std")] + pub fn rotation_y(degree: f32) -> Self { + let (sin, cos) = degree.to_radians().sin_cos(); + Self { + M11: cos, + M12: 0.0, + M13: -sin, + M14: 0.0, + M21: 0.0, + M22: 1.0, + M23: 0.0, + M24: 0.0, + M31: sin, + M32: 0.0, + M33: cos, + M34: 0.0, + M41: 0.0, + M42: 0.0, + M43: 0.0, + M44: 1.0, + } + } + pub fn perspective_projection(depth: f32) -> Self { + let projection = if depth > 0.0 { -1.0 / depth } else { 0.0 }; + Self { + M11: 1.0, + M12: 0.0, + M13: 0.0, + M14: 0.0, + M21: 0.0, + M22: 1.0, + M23: 0.0, + M24: 0.0, + M31: 0.0, + M32: 0.0, + M33: 1.0, + M34: projection, + M41: 0.0, + M42: 0.0, + M43: 0.0, + M44: 1.0, + } + } + fn impl_add(&self, rhs: &Self) -> Self { + Self { + M11: self.M11 + rhs.M11, + M12: self.M12 + rhs.M12, + M13: self.M13 + rhs.M13, + M14: self.M14 + rhs.M14, + M21: self.M21 + rhs.M21, + M22: self.M22 + rhs.M22, + M23: self.M23 + rhs.M23, + M24: self.M24 + rhs.M24, + M31: self.M31 + rhs.M31, + M32: self.M32 + rhs.M32, + M33: self.M33 + rhs.M33, + M34: self.M34 + rhs.M34, + M41: self.M41 + rhs.M41, + M42: self.M42 + rhs.M42, + M43: self.M43 + rhs.M43, + M44: self.M44 + rhs.M44, + } + } + fn impl_sub(&self, rhs: &Self) -> Self { + Self { + M11: self.M11 - rhs.M11, + M12: self.M12 - rhs.M12, + M13: self.M13 - rhs.M13, + M14: self.M14 - rhs.M14, + M21: self.M21 - rhs.M21, + M22: self.M22 - rhs.M22, + M23: self.M23 - rhs.M23, + M24: self.M24 - rhs.M24, + M31: self.M31 - rhs.M31, + M32: self.M32 - rhs.M32, + M33: self.M33 - rhs.M33, + M34: self.M34 - rhs.M34, + M41: self.M41 - rhs.M41, + M42: self.M42 - rhs.M42, + M43: self.M43 - rhs.M43, + M44: self.M44 - rhs.M44, + } + } + fn impl_mul(&self, rhs: &Self) -> Self { + Self { + M11: self.M11 * rhs.M11 + self.M12 * rhs.M21 + self.M13 * rhs.M31 + self.M14 * rhs.M41, + M12: self.M11 * rhs.M12 + self.M12 * rhs.M22 + self.M13 * rhs.M32 + self.M14 * rhs.M42, + M13: self.M11 * rhs.M13 + self.M12 * rhs.M23 + self.M13 * rhs.M33 + self.M14 * rhs.M43, + M14: self.M11 * rhs.M14 + self.M12 * rhs.M24 + self.M13 * rhs.M34 + self.M14 * rhs.M44, + M21: self.M21 * rhs.M11 + self.M22 * rhs.M21 + self.M23 * rhs.M31 + self.M24 * rhs.M41, + M22: self.M21 * rhs.M12 + self.M22 * rhs.M22 + self.M23 * rhs.M32 + self.M24 * rhs.M42, + M23: self.M21 * rhs.M13 + self.M22 * rhs.M23 + self.M23 * rhs.M33 + self.M24 * rhs.M43, + M24: self.M21 * rhs.M14 + self.M22 * rhs.M24 + self.M23 * rhs.M34 + self.M24 * rhs.M44, + M31: self.M31 * rhs.M11 + self.M32 * rhs.M21 + self.M33 * rhs.M31 + self.M34 * rhs.M41, + M32: self.M31 * rhs.M12 + self.M32 * rhs.M22 + self.M33 * rhs.M32 + self.M34 * rhs.M42, + M33: self.M31 * rhs.M13 + self.M32 * rhs.M23 + self.M33 * rhs.M33 + self.M34 * rhs.M43, + M34: self.M31 * rhs.M14 + self.M32 * rhs.M24 + self.M33 * rhs.M34 + self.M34 * rhs.M44, + M41: self.M41 * rhs.M11 + self.M42 * rhs.M21 + self.M43 * rhs.M31 + self.M44 * rhs.M41, + M42: self.M41 * rhs.M12 + self.M42 * rhs.M22 + self.M43 * rhs.M32 + self.M44 * rhs.M42, + M43: self.M41 * rhs.M13 + self.M42 * rhs.M23 + self.M43 * rhs.M33 + self.M44 * rhs.M43, + M44: self.M41 * rhs.M14 + self.M42 * rhs.M24 + self.M43 * rhs.M34 + self.M44 * rhs.M44, + } + } + fn impl_mul_f32(&self, rhs: f32) -> Self { + Self { + M11: self.M11 * rhs, + M12: self.M12 * rhs, + M13: self.M13 * rhs, + M14: self.M14 * rhs, + M21: self.M21 * rhs, + M22: self.M22 * rhs, + M23: self.M23 * rhs, + M24: self.M24 * rhs, + M31: self.M31 * rhs, + M32: self.M32 * rhs, + M33: self.M33 * rhs, + M34: self.M34 * rhs, + M41: self.M41 * rhs, + M42: self.M42 * rhs, + M43: self.M43 * rhs, + M44: self.M44 * rhs, + } + } +} + +impl core::ops::Add for Matrix4x4 { + type Output = Self; + fn add(self, rhs: Self) -> Self { + self.impl_add(&rhs) + } +} +impl core::ops::Add<&Self> for Matrix4x4 { + type Output = Self; + fn add(self, rhs: &Self) -> Self { + self.impl_add(rhs) + } +} +impl core::ops::Add for &Matrix4x4 { + type Output = Matrix4x4; + fn add(self, rhs: Matrix4x4) -> Matrix4x4 { + self.impl_add(&rhs) + } +} +impl core::ops::Add<&Matrix4x4> for &Matrix4x4 { + type Output = Matrix4x4; + fn add(self, rhs: &Matrix4x4) -> Matrix4x4 { + self.impl_add(rhs) + } +} +impl core::ops::Sub for Matrix4x4 { + type Output = Self; + fn sub(self, rhs: Self) -> Self { + self.impl_sub(&rhs) + } +} +impl core::ops::Sub<&Self> for Matrix4x4 { + type Output = Self; + fn sub(self, rhs: &Self) -> Self { + self.impl_sub(rhs) + } +} +impl core::ops::Sub for &Matrix4x4 { + type Output = Matrix4x4; + fn sub(self, rhs: Matrix4x4) -> Matrix4x4 { + self.impl_sub(&rhs) + } +} +impl core::ops::Sub<&Matrix4x4> for &Matrix4x4 { + type Output = Matrix4x4; + fn sub(self, rhs: &Matrix4x4) -> Matrix4x4 { + self.impl_sub(rhs) + } +} +impl core::ops::Mul for Matrix4x4 { + type Output = Self; + fn mul(self, rhs: Self) -> Self { + self.impl_mul(&rhs) + } +} +impl core::ops::Mul<&Self> for Matrix4x4 { + type Output = Self; + fn mul(self, rhs: &Self) -> Self { + self.impl_mul(rhs) + } +} +impl core::ops::Mul for &Matrix4x4 { + type Output = Matrix4x4; + fn mul(self, rhs: Matrix4x4) -> Matrix4x4 { + self.impl_mul(&rhs) + } +} +impl core::ops::Mul<&Matrix4x4> for &Matrix4x4 { + type Output = Matrix4x4; + fn mul(self, rhs: &Matrix4x4) -> Matrix4x4 { + self.impl_mul(rhs) + } +} +impl core::ops::Mul for Matrix4x4 { + type Output = Self; + fn mul(self, rhs: f32) -> Self { + self.impl_mul_f32(rhs) + } +} +impl core::ops::Mul for &Matrix4x4 { + type Output = Matrix4x4; + fn mul(self, rhs: f32) -> Matrix4x4 { + self.impl_mul_f32(rhs) + } +} diff --git a/third_party/windows-winui/windows-numerics/src/vector2.rs b/third_party/windows-winui/windows-numerics/src/vector2.rs new file mode 100644 index 0000000000..39181329dd --- /dev/null +++ b/third_party/windows-winui/windows-numerics/src/vector2.rs @@ -0,0 +1,216 @@ +use super::*; + +impl Vector2 { + pub fn new(X: f32, Y: f32) -> Self { + Self { X, Y } + } + pub fn zero() -> Self { + Self { X: 0f32, Y: 0f32 } + } + pub fn one() -> Self { + Self { X: 1f32, Y: 1f32 } + } + pub fn unit_x() -> Self { + Self { X: 1.0, Y: 0.0 } + } + pub fn unit_y() -> Self { + Self { X: 0.0, Y: 1.0 } + } + pub fn dot(&self, rhs: &Self) -> f32 { + self.X * rhs.X + self.Y * rhs.Y + } + pub fn length_squared(&self) -> f32 { + self.dot(self) + } + #[cfg(feature = "std")] + pub fn length(&self) -> f32 { + self.length_squared().sqrt() + } + #[cfg(feature = "std")] + pub fn distance(&self, value: &Self) -> f32 { + (self - value).length() + } + pub fn distance_squared(&self, value: &Self) -> f32 { + (self - value).length_squared() + } + #[cfg(feature = "std")] + pub fn normalize(&self) -> Self { + self / self.length() + } + + fn impl_neg(&self) -> Self { + Self { + X: -self.X, + Y: -self.Y, + } + } + fn impl_add(&self, rhs: &Self) -> Self { + Self { + X: self.X + rhs.X, + Y: self.Y + rhs.Y, + } + } + fn impl_sub(&self, rhs: &Self) -> Self { + Self { + X: self.X - rhs.X, + Y: self.Y - rhs.Y, + } + } + fn impl_div(&self, rhs: &Self) -> Self { + Self { + X: self.X / rhs.X, + Y: self.Y / rhs.Y, + } + } + fn impl_div_f32(&self, rhs: f32) -> Self { + Self { + X: self.X / rhs, + Y: self.Y / rhs, + } + } + fn impl_mul(&self, rhs: &Self) -> Self { + Self { + X: self.X * rhs.X, + Y: self.Y * rhs.Y, + } + } + fn impl_mul_f32(&self, rhs: f32) -> Self { + Self { + X: self.X * rhs, + Y: self.Y * rhs, + } + } +} + +impl core::ops::Neg for Vector2 { + type Output = Self; + fn neg(self) -> Self { + self.impl_neg() + } +} +impl core::ops::Neg for &Vector2 { + type Output = Vector2; + fn neg(self) -> Vector2 { + self.impl_neg() + } +} +impl core::ops::Add for Vector2 { + type Output = Self; + fn add(self, rhs: Self) -> Self { + self.impl_add(&rhs) + } +} +impl core::ops::Add<&Self> for Vector2 { + type Output = Self; + fn add(self, rhs: &Self) -> Self { + self.impl_add(rhs) + } +} +impl core::ops::Add for &Vector2 { + type Output = Vector2; + fn add(self, rhs: Vector2) -> Vector2 { + self.impl_add(&rhs) + } +} +impl core::ops::Add<&Vector2> for &Vector2 { + type Output = Vector2; + fn add(self, rhs: &Vector2) -> Vector2 { + self.impl_add(rhs) + } +} +impl core::ops::Sub for Vector2 { + type Output = Self; + fn sub(self, rhs: Self) -> Self { + self.impl_sub(&rhs) + } +} +impl core::ops::Sub<&Self> for Vector2 { + type Output = Self; + fn sub(self, rhs: &Self) -> Self { + self.impl_sub(rhs) + } +} +impl core::ops::Sub for &Vector2 { + type Output = Vector2; + fn sub(self, rhs: Vector2) -> Vector2 { + self.impl_sub(&rhs) + } +} +impl core::ops::Sub<&Vector2> for &Vector2 { + type Output = Vector2; + fn sub(self, rhs: &Vector2) -> Vector2 { + self.impl_sub(rhs) + } +} +impl core::ops::Div for Vector2 { + type Output = Self; + fn div(self, rhs: Self) -> Self { + self.impl_div(&rhs) + } +} +impl core::ops::Div<&Self> for Vector2 { + type Output = Self; + fn div(self, rhs: &Self) -> Self { + self.impl_div(rhs) + } +} +impl core::ops::Div for &Vector2 { + type Output = Vector2; + fn div(self, rhs: Vector2) -> Vector2 { + self.impl_div(&rhs) + } +} +impl core::ops::Div<&Vector2> for &Vector2 { + type Output = Vector2; + fn div(self, rhs: &Vector2) -> Vector2 { + self.impl_div(rhs) + } +} +impl core::ops::Div for Vector2 { + type Output = Self; + fn div(self, rhs: f32) -> Self { + self.impl_div_f32(rhs) + } +} +impl core::ops::Div for &Vector2 { + type Output = Vector2; + fn div(self, rhs: f32) -> Vector2 { + self.impl_div_f32(rhs) + } +} +impl core::ops::Mul for Vector2 { + type Output = Self; + fn mul(self, rhs: Self) -> Self { + self.impl_mul(&rhs) + } +} +impl core::ops::Mul<&Self> for Vector2 { + type Output = Self; + fn mul(self, rhs: &Self) -> Self { + self.impl_mul(rhs) + } +} +impl core::ops::Mul for &Vector2 { + type Output = Vector2; + fn mul(self, rhs: Vector2) -> Vector2 { + self.impl_mul(&rhs) + } +} +impl core::ops::Mul<&Vector2> for &Vector2 { + type Output = Vector2; + fn mul(self, rhs: &Vector2) -> Vector2 { + self.impl_mul(rhs) + } +} +impl core::ops::Mul for Vector2 { + type Output = Self; + fn mul(self, rhs: f32) -> Self { + self.impl_mul_f32(rhs) + } +} +impl core::ops::Mul for &Vector2 { + type Output = Vector2; + fn mul(self, rhs: f32) -> Vector2 { + self.impl_mul_f32(rhs) + } +} diff --git a/third_party/windows-winui/windows-numerics/src/vector3.rs b/third_party/windows-winui/windows-numerics/src/vector3.rs new file mode 100644 index 0000000000..88d38245cd --- /dev/null +++ b/third_party/windows-winui/windows-numerics/src/vector3.rs @@ -0,0 +1,254 @@ +use super::*; + +impl Vector3 { + pub fn new(X: f32, Y: f32, Z: f32) -> Self { + Self { X, Y, Z } + } + pub fn zero() -> Self { + Self { + X: 0f32, + Y: 0f32, + Z: 0f32, + } + } + pub fn one() -> Self { + Self { + X: 1f32, + Y: 1f32, + Z: 1f32, + } + } + pub fn unit_x() -> Self { + Self { + X: 1.0, + Y: 0.0, + Z: 0.0, + } + } + pub fn unit_y() -> Self { + Self { + X: 0.0, + Y: 1.0, + Z: 0.0, + } + } + pub fn unit_z() -> Self { + Self { + X: 0.0, + Y: 0.0, + Z: 1.0, + } + } + pub fn dot(&self, rhs: &Self) -> f32 { + self.X * rhs.X + self.Y * rhs.Y + self.Z * rhs.Z + } + pub fn length_squared(&self) -> f32 { + self.dot(self) + } + #[cfg(feature = "std")] + pub fn length(&self) -> f32 { + self.length_squared().sqrt() + } + #[cfg(feature = "std")] + pub fn distance(&self, value: &Self) -> f32 { + (self - value).length() + } + pub fn distance_squared(&self, value: &Self) -> f32 { + (self - value).length_squared() + } + #[cfg(feature = "std")] + pub fn normalize(&self) -> Self { + self / self.length() + } + + pub fn cross(&self, rhs: &Self) -> Self { + Self { + X: self.Y * rhs.Z - self.Z * rhs.Y, + Y: self.Z * rhs.X - self.X * rhs.Z, + Z: self.X * rhs.Y - self.Y * rhs.X, + } + } + + fn impl_neg(&self) -> Self { + Self { + X: -self.X, + Y: -self.Y, + Z: -self.Z, + } + } + fn impl_add(&self, rhs: &Self) -> Self { + Self { + X: self.X + rhs.X, + Y: self.Y + rhs.Y, + Z: self.Z + rhs.Z, + } + } + fn impl_sub(&self, rhs: &Self) -> Self { + Self { + X: self.X - rhs.X, + Y: self.Y - rhs.Y, + Z: self.Z - rhs.Z, + } + } + fn impl_div(&self, rhs: &Self) -> Self { + Self { + X: self.X / rhs.X, + Y: self.Y / rhs.Y, + Z: self.Z / rhs.Z, + } + } + fn impl_div_f32(&self, rhs: f32) -> Self { + Self { + X: self.X / rhs, + Y: self.Y / rhs, + Z: self.Z / rhs, + } + } + fn impl_mul(&self, rhs: &Self) -> Self { + Self { + X: self.X * rhs.X, + Y: self.Y * rhs.Y, + Z: self.Z * rhs.Z, + } + } + fn impl_mul_f32(&self, rhs: f32) -> Self { + Self { + X: self.X * rhs, + Y: self.Y * rhs, + Z: self.Z * rhs, + } + } +} + +impl core::ops::Neg for Vector3 { + type Output = Self; + fn neg(self) -> Self { + self.impl_neg() + } +} +impl core::ops::Neg for &Vector3 { + type Output = Vector3; + fn neg(self) -> Vector3 { + self.impl_neg() + } +} +impl core::ops::Add for Vector3 { + type Output = Self; + fn add(self, rhs: Self) -> Self { + self.impl_add(&rhs) + } +} +impl core::ops::Add<&Self> for Vector3 { + type Output = Self; + fn add(self, rhs: &Self) -> Self { + self.impl_add(rhs) + } +} +impl core::ops::Add for &Vector3 { + type Output = Vector3; + fn add(self, rhs: Vector3) -> Vector3 { + self.impl_add(&rhs) + } +} +impl core::ops::Add<&Vector3> for &Vector3 { + type Output = Vector3; + fn add(self, rhs: &Vector3) -> Vector3 { + self.impl_add(rhs) + } +} +impl core::ops::Sub for Vector3 { + type Output = Self; + fn sub(self, rhs: Self) -> Self { + self.impl_sub(&rhs) + } +} +impl core::ops::Sub<&Self> for Vector3 { + type Output = Self; + fn sub(self, rhs: &Self) -> Self { + self.impl_sub(rhs) + } +} +impl core::ops::Sub for &Vector3 { + type Output = Vector3; + fn sub(self, rhs: Vector3) -> Vector3 { + self.impl_sub(&rhs) + } +} +impl core::ops::Sub<&Vector3> for &Vector3 { + type Output = Vector3; + fn sub(self, rhs: &Vector3) -> Vector3 { + self.impl_sub(rhs) + } +} +impl core::ops::Div for Vector3 { + type Output = Self; + fn div(self, rhs: Self) -> Self { + self.impl_div(&rhs) + } +} +impl core::ops::Div<&Self> for Vector3 { + type Output = Self; + fn div(self, rhs: &Self) -> Self { + self.impl_div(rhs) + } +} +impl core::ops::Div for &Vector3 { + type Output = Vector3; + fn div(self, rhs: Vector3) -> Vector3 { + self.impl_div(&rhs) + } +} +impl core::ops::Div<&Vector3> for &Vector3 { + type Output = Vector3; + fn div(self, rhs: &Vector3) -> Vector3 { + self.impl_div(rhs) + } +} +impl core::ops::Div for Vector3 { + type Output = Self; + fn div(self, rhs: f32) -> Self { + self.impl_div_f32(rhs) + } +} +impl core::ops::Div for &Vector3 { + type Output = Vector3; + fn div(self, rhs: f32) -> Vector3 { + self.impl_div_f32(rhs) + } +} +impl core::ops::Mul for Vector3 { + type Output = Self; + fn mul(self, rhs: Self) -> Self { + self.impl_mul(&rhs) + } +} +impl core::ops::Mul<&Self> for Vector3 { + type Output = Self; + fn mul(self, rhs: &Self) -> Self { + self.impl_mul(rhs) + } +} +impl core::ops::Mul for &Vector3 { + type Output = Vector3; + fn mul(self, rhs: Vector3) -> Vector3 { + self.impl_mul(&rhs) + } +} +impl core::ops::Mul<&Vector3> for &Vector3 { + type Output = Vector3; + fn mul(self, rhs: &Vector3) -> Vector3 { + self.impl_mul(rhs) + } +} +impl core::ops::Mul for Vector3 { + type Output = Self; + fn mul(self, rhs: f32) -> Self { + self.impl_mul_f32(rhs) + } +} +impl core::ops::Mul for &Vector3 { + type Output = Vector3; + fn mul(self, rhs: f32) -> Vector3 { + self.impl_mul_f32(rhs) + } +} diff --git a/third_party/windows-winui/windows-numerics/src/vector4.rs b/third_party/windows-winui/windows-numerics/src/vector4.rs new file mode 100644 index 0000000000..69947fac1b --- /dev/null +++ b/third_party/windows-winui/windows-numerics/src/vector4.rs @@ -0,0 +1,266 @@ +use super::*; + +impl Vector4 { + pub fn new(X: f32, Y: f32, Z: f32, W: f32) -> Self { + Self { X, Y, Z, W } + } + pub fn zero() -> Self { + Self { + X: 0f32, + Y: 0f32, + Z: 0f32, + W: 0f32, + } + } + pub fn one() -> Self { + Self { + X: 1f32, + Y: 1f32, + Z: 1f32, + W: 1f32, + } + } + pub fn unit_x() -> Self { + Self { + X: 1.0, + Y: 0.0, + Z: 0.0, + W: 0.0, + } + } + pub fn unit_y() -> Self { + Self { + X: 0.0, + Y: 1.0, + Z: 0.0, + W: 0.0, + } + } + pub fn unit_z() -> Self { + Self { + X: 0.0, + Y: 0.0, + Z: 1.0, + W: 0.0, + } + } + pub fn unit_w() -> Self { + Self { + X: 0.0, + Y: 0.0, + Z: 0.0, + W: 1.0, + } + } + pub fn dot(&self, rhs: &Self) -> f32 { + self.X * rhs.X + self.Y * rhs.Y + self.Z * rhs.Z + self.W * rhs.W + } + pub fn length_squared(&self) -> f32 { + self.dot(self) + } + #[cfg(feature = "std")] + pub fn length(&self) -> f32 { + self.length_squared().sqrt() + } + #[cfg(feature = "std")] + pub fn distance(&self, value: &Self) -> f32 { + (self - value).length() + } + pub fn distance_squared(&self, value: &Self) -> f32 { + (self - value).length_squared() + } + #[cfg(feature = "std")] + pub fn normalize(&self) -> Self { + self / self.length() + } + + fn impl_neg(&self) -> Self { + Self { + X: -self.X, + Y: -self.Y, + Z: -self.Z, + W: -self.W, + } + } + fn impl_add(&self, rhs: &Self) -> Self { + Self { + X: self.X + rhs.X, + Y: self.Y + rhs.Y, + Z: self.Z + rhs.Z, + W: self.W + rhs.W, + } + } + fn impl_sub(&self, rhs: &Self) -> Self { + Self { + X: self.X - rhs.X, + Y: self.Y - rhs.Y, + Z: self.Z - rhs.Z, + W: self.W - rhs.W, + } + } + fn impl_div(&self, rhs: &Self) -> Self { + Self { + X: self.X / rhs.X, + Y: self.Y / rhs.Y, + Z: self.Z / rhs.Z, + W: self.W / rhs.W, + } + } + fn impl_div_f32(&self, rhs: f32) -> Self { + Self { + X: self.X / rhs, + Y: self.Y / rhs, + Z: self.Z / rhs, + W: self.W / rhs, + } + } + fn impl_mul(&self, rhs: &Self) -> Self { + Self { + X: self.X * rhs.X, + Y: self.Y * rhs.Y, + Z: self.Z * rhs.Z, + W: self.W * rhs.W, + } + } + fn impl_mul_f32(&self, rhs: f32) -> Self { + Self { + X: self.X * rhs, + Y: self.Y * rhs, + Z: self.Z * rhs, + W: self.W * rhs, + } + } +} + +impl core::ops::Neg for Vector4 { + type Output = Self; + fn neg(self) -> Self { + self.impl_neg() + } +} +impl core::ops::Neg for &Vector4 { + type Output = Vector4; + fn neg(self) -> Vector4 { + self.impl_neg() + } +} +impl core::ops::Add for Vector4 { + type Output = Self; + fn add(self, rhs: Self) -> Self { + self.impl_add(&rhs) + } +} +impl core::ops::Add<&Self> for Vector4 { + type Output = Self; + fn add(self, rhs: &Self) -> Self { + self.impl_add(rhs) + } +} +impl core::ops::Add for &Vector4 { + type Output = Vector4; + fn add(self, rhs: Vector4) -> Vector4 { + self.impl_add(&rhs) + } +} +impl core::ops::Add<&Vector4> for &Vector4 { + type Output = Vector4; + fn add(self, rhs: &Vector4) -> Vector4 { + self.impl_add(rhs) + } +} +impl core::ops::Sub for Vector4 { + type Output = Self; + fn sub(self, rhs: Self) -> Self { + self.impl_sub(&rhs) + } +} +impl core::ops::Sub<&Self> for Vector4 { + type Output = Self; + fn sub(self, rhs: &Self) -> Self { + self.impl_sub(rhs) + } +} +impl core::ops::Sub for &Vector4 { + type Output = Vector4; + fn sub(self, rhs: Vector4) -> Vector4 { + self.impl_sub(&rhs) + } +} +impl core::ops::Sub<&Vector4> for &Vector4 { + type Output = Vector4; + fn sub(self, rhs: &Vector4) -> Vector4 { + self.impl_sub(rhs) + } +} +impl core::ops::Div for Vector4 { + type Output = Self; + fn div(self, rhs: Self) -> Self { + self.impl_div(&rhs) + } +} +impl core::ops::Div<&Self> for Vector4 { + type Output = Self; + fn div(self, rhs: &Self) -> Self { + self.impl_div(rhs) + } +} +impl core::ops::Div for &Vector4 { + type Output = Vector4; + fn div(self, rhs: Vector4) -> Vector4 { + self.impl_div(&rhs) + } +} +impl core::ops::Div<&Vector4> for &Vector4 { + type Output = Vector4; + fn div(self, rhs: &Vector4) -> Vector4 { + self.impl_div(rhs) + } +} +impl core::ops::Div for Vector4 { + type Output = Self; + fn div(self, rhs: f32) -> Self { + self.impl_div_f32(rhs) + } +} +impl core::ops::Div for &Vector4 { + type Output = Vector4; + fn div(self, rhs: f32) -> Vector4 { + self.impl_div_f32(rhs) + } +} +impl core::ops::Mul for Vector4 { + type Output = Self; + fn mul(self, rhs: Self) -> Self { + self.impl_mul(&rhs) + } +} +impl core::ops::Mul<&Self> for Vector4 { + type Output = Self; + fn mul(self, rhs: &Self) -> Self { + self.impl_mul(rhs) + } +} +impl core::ops::Mul for &Vector4 { + type Output = Vector4; + fn mul(self, rhs: Vector4) -> Vector4 { + self.impl_mul(&rhs) + } +} +impl core::ops::Mul<&Vector4> for &Vector4 { + type Output = Vector4; + fn mul(self, rhs: &Vector4) -> Vector4 { + self.impl_mul(rhs) + } +} +impl core::ops::Mul for Vector4 { + type Output = Self; + fn mul(self, rhs: f32) -> Self { + self.impl_mul_f32(rhs) + } +} +impl core::ops::Mul for &Vector4 { + type Output = Vector4; + fn mul(self, rhs: f32) -> Vector4 { + self.impl_mul_f32(rhs) + } +} diff --git a/third_party/windows-winui/windows-reactor/Cargo.toml b/third_party/windows-winui/windows-reactor/Cargo.toml new file mode 100644 index 0000000000..93b61e6338 --- /dev/null +++ b/third_party/windows-winui/windows-reactor/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "windows-reactor" +version = "0.0.0" +edition = "2021" +rust-version = "1.95" +license = "MIT OR Apache-2.0" +description = "Windows UI library" +repository = "https://github.com/microsoft/windows-rs" +categories = ["os::windows-apis"] +readme = "readme.md" + +[features] +default = ["diagnostics"] +diagnostics = [] + +[dependencies] +rustc-hash = "2" +windows-core = { path = "../windows-core" } +windows-numerics = { path = "../windows-numerics" } +windows-collections = { path = "../windows-collections", features = ["std"] } +windows-future = { path = "../windows-future" } +windows-reference = { path = "../windows-reference", features = ["std"] } +windows-threading = { path = "../windows-threading", features = ["std"] } +windows-time = { path = "../windows-time" } diff --git a/third_party/windows-winui/windows-reactor/build.rs b/third_party/windows-winui/windows-reactor/build.rs new file mode 100644 index 0000000000..e6b51a6aee --- /dev/null +++ b/third_party/windows-winui/windows-reactor/build.rs @@ -0,0 +1,242 @@ +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; +use std::process::Command; + +const WINAPPSDK_FOUNDATION_PACKAGE: &str = "Microsoft.WindowsAppSDK.Foundation"; +const WINAPPSDK_FOUNDATION_VERSION: &str = "2.0.21"; +const WINAPPSDK_INTERACTIVE_PACKAGE: &str = "Microsoft.WindowsAppSDK.InteractiveExperiences"; +const WINAPPSDK_INTERACTIVE_VERSION: &str = "2.0.13"; +const NUGET_URL_TEMPLATE: &str = "https://www.nuget.org/api/v2/package/{name}/{version}"; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + if env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") { + return; + } + + let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR set by cargo")); + let temp_dir = out_dir.join("winappsdk-packages"); + fs::create_dir_all(&temp_dir).expect("Failed to create temp directory"); + + let foundation_dir = stage_package( + WINAPPSDK_FOUNDATION_PACKAGE, + WINAPPSDK_FOUNDATION_VERSION, + &temp_dir, + ); + let interactive_dir = stage_package( + WINAPPSDK_INTERACTIVE_PACKAGE, + WINAPPSDK_INTERACTIVE_VERSION, + &temp_dir, + ); + let target_dir = target_dir_from_out(&out_dir); + let dest_dirs = [ + target_dir.clone(), + target_dir.join("examples"), + target_dir.join("deps"), + ]; + for dest_dir in &dest_dirs { + fs::create_dir_all(dest_dir).expect("Failed to create runtime asset directory"); + } + + let arch = format!("win-{}", target_arch()); + let bootstrap_src = foundation_dir + .join(&arch) + .join("native") + .join("Microsoft.WindowsAppRuntime.Bootstrap.dll"); + let bootstrap_import_src = foundation_dir + .join("native") + .join(target_arch()) + .join("Microsoft.WindowsAppRuntime.Bootstrap.lib"); + + if bootstrap_src.is_file() { + println!("cargo:rerun-if-changed={}", bootstrap_src.display()); + for dest_dir in &dest_dirs { + let dest = dest_dir.join("Microsoft.WindowsAppRuntime.Bootstrap.dll"); + if let Err(e) = fs::copy(&bootstrap_src, &dest) { + println!( + "cargo:warning=Failed to copy bootstrap DLL to {}: {}", + dest.display(), + e + ); + } + } + } else { + println!( + "cargo:warning=Microsoft.WindowsAppRuntime.Bootstrap.dll not found at {}", + bootstrap_src.display() + ); + } + + if bootstrap_import_src.is_file() { + println!("cargo:rerun-if-changed={}", bootstrap_import_src.display()); + for dest_dir in &dest_dirs { + let dest = dest_dir.join("Microsoft.WindowsAppRuntime.Bootstrap.lib"); + if let Err(e) = fs::copy(&bootstrap_import_src, &dest) { + println!( + "cargo:warning=Failed to copy bootstrap import library to {}: {}", + dest.display(), + e + ); + } + } + } else { + println!( + "cargo:warning=Microsoft.WindowsAppRuntime.Bootstrap.lib not found at {}", + bootstrap_import_src.display() + ); + } + + let pri_src = interactive_dir + .join(&arch) + .join("native") + .join("Microsoft.UI.pri"); + + if pri_src.is_file() { + println!("cargo:rerun-if-changed={}", pri_src.display()); + for dest_dir in &dest_dirs { + let dest = dest_dir.join("resources.pri"); + if let Err(e) = fs::copy(&pri_src, &dest) { + println!( + "cargo:warning=Failed to copy framework PRI to {}: {}", + dest.display(), + e + ); + } + } + } else { + println!( + "cargo:warning=Microsoft.UI.pri not found at {}. \ + XamlControlsResources will fail to load unless a resources.pri \ + file is present next to the executable.", + pri_src.display() + ); + } +} + +fn stage_package(name: &str, version: &str, temp_dir: &Path) -> PathBuf { + let nupkg_path = temp_dir.join(format!("{name}.{version}.nupkg")); + let extract_dir = temp_dir.join(format!("{name}-{version}")); + + if !nupkg_path.is_file() { + download_nupkg(name, version, &nupkg_path); + } + + if !extract_dir.is_dir() { + fs::create_dir_all(&extract_dir).expect("Failed to create extract directory"); + extract_archive(&nupkg_path, &extract_dir, &["--strip-components=1"]); + } + + if !extract_dir.is_dir() || fs::read_dir(&extract_dir).map_or(true, |r| r.count() == 0) { + println!( + "cargo:warning=Extraction of {} to {} produced no files", + nupkg_path.display(), + extract_dir.display() + ); + } + + extract_dir +} + +fn download_nupkg(name: &str, version: &str, dest: &Path) { + let url = NUGET_URL_TEMPLATE + .replace("{name}", name) + .replace("{version}", version); + + println!("cargo:warning=Downloading {name} version {version} from {url}"); + + let curl_path = windows_system32().join("curl.exe"); + if !curl_path.is_file() { + println!( + "cargo:warning=curl.exe not found at {}", + curl_path.display() + ); + return; + } + + let status = Command::new(&curl_path) + .args([ + "-s", + "-L", + "-o", + dest.to_str().expect("invalid dest path"), + &url, + ]) + .status(); + + match status { + Ok(s) if s.success() => { + println!("cargo:warning=Downloaded {name} version {version} successfully"); + } + Ok(s) => { + println!( + "cargo:warning=Failed to download {name} version {version}: exit code {}", + s.code().unwrap_or(-1) + ); + } + Err(e) => { + println!("cargo:warning=Failed to run curl: {e}"); + } + } +} + +fn target_dir_from_out(out_dir: &Path) -> PathBuf { + out_dir + .ancestors() + .find(|path| path.file_name().is_some_and(|name| name == "build")) + .and_then(Path::parent) + .unwrap_or(out_dir) + .to_path_buf() +} + +fn windows_system32() -> PathBuf { + PathBuf::from(env::var("SystemRoot").unwrap()).join("System32") +} + +fn target_arch() -> &'static str { + match env::var("CARGO_CFG_TARGET_ARCH").as_deref() { + Ok("aarch64") => "arm64", + Ok("x86") => "x86", + _ => "x64", + } +} + +fn extract_archive(archive_path: &Path, dest_path: &Path, extra_args: &[&str]) { + println!( + "cargo:warning=Extracting archive {} to {}", + archive_path.display(), + dest_path.display() + ); + + let tar_path = windows_system32().join("tar.exe"); + if !tar_path.is_file() { + println!("cargo:warning=tar.exe not found at {}", tar_path.display()); + return; + } + + let status = Command::new(&tar_path) + .args([ + "-xf", + archive_path.to_str().expect("invalid archive path"), + "-C", + dest_path.to_str().expect("invalid destination path"), + ]) + .args(extra_args) + .status(); + + match status { + Ok(s) if s.success() => { + println!("cargo:warning=Extracted archive successfully"); + } + Ok(s) => { + println!( + "cargo:warning=Failed to extract archive: {}", + s.code().unwrap_or(-1) + ); + } + Err(e) => { + println!("cargo:warning=Failed to run tar: {e}"); + } + } +} diff --git a/third_party/windows-winui/windows-reactor/license-apache-2.0 b/third_party/windows-winui/windows-reactor/license-apache-2.0 new file mode 100644 index 0000000000..b5ed4ecec2 --- /dev/null +++ b/third_party/windows-winui/windows-reactor/license-apache-2.0 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) Microsoft Corporation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/windows-winui/windows-reactor/license-mit b/third_party/windows-winui/windows-reactor/license-mit new file mode 100644 index 0000000000..9e841e7a26 --- /dev/null +++ b/third_party/windows-winui/windows-reactor/license-mit @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/third_party/windows-winui/windows-reactor/readme.md b/third_party/windows-winui/windows-reactor/readme.md new file mode 100644 index 0000000000..12d8404ba3 --- /dev/null +++ b/third_party/windows-winui/windows-reactor/readme.md @@ -0,0 +1 @@ +## Windows Reactor is a UI library for Rust developers backed by WinUI diff --git a/third_party/windows-winui/windows-reactor/src/app.rs b/third_party/windows-winui/windows-reactor/src/app.rs new file mode 100644 index 0000000000..f6ec23447f --- /dev/null +++ b/third_party/windows-winui/windows-reactor/src/app.rs @@ -0,0 +1,350 @@ +use std::cell::RefCell; +use std::panic::AssertUnwindSafe; +use std::sync::{Arc, Mutex}; + +use windows_core::{Error, Result, HRESULT}; + +use super::app_shim::*; +use super::bindings::*; +use super::winui::*; + +const E_FAIL: HRESULT = HRESULT(0x80004005u32 as i32); + +thread_local! { + + static HOST_SLOT: RefCell> = const { RefCell::new(None) }; + + static APP_SLOT: RefCell> = + const { RefCell::new(None) }; +} + +/// Run `f` with the [`ReactorHost`] for the current thread, if any. +pub fn with_active_host(f: F) -> Option +where + F: FnOnce(&ReactorHost) -> R, +{ + HOST_SLOT.with(|slot| slot.borrow().as_ref().map(f)) +} + +/// Top-level reactor application; bootstraps WinAppSDK, installs XAML +/// resources, and hosts a single root [`Component`]. +pub struct App { + title: Option, + inner_size: Option, + inner_constraints: InnerConstraints, + eager_templated_realization: bool, + presenter: PresenterKind, + backdrop: Option, + on_exit: Option>, +} + +impl Default for App { + fn default() -> Self { + Self::new() + } +} + +impl App { + pub fn new() -> Self { + Self { + title: None, + inner_size: None, + inner_constraints: InnerConstraints::default(), + eager_templated_realization: false, + presenter: PresenterKind::Default, + backdrop: None, + on_exit: None, + } + } + + pub fn title(mut self, title: impl Into) -> Self { + self.title = Some(title.into()); + self + } + + pub fn inner_size(mut self, width: f64, height: f64) -> Self { + self.inner_size = Some(crate::core::Size { width, height }); + self + } + + pub fn inner_constraints(mut self, constraints: InnerConstraints) -> Self { + self.inner_constraints = constraints; + self + } + + pub fn eager_templated_realization(mut self, on: bool) -> Self { + self.eager_templated_realization = on; + self + } + + /// Set the top-level window presenter (defaults to overlapped window). + pub fn presenter(mut self, kind: PresenterKind) -> Self { + self.presenter = kind; + self + } + + /// Shortcut for `presenter(PresenterKind::FullScreen)`. + pub fn fullscreen(self, on: bool) -> Self { + self.presenter(if on { + PresenterKind::FullScreen + } else { + PresenterKind::Default + }) + } + + /// Set the system backdrop for the window created by [`App::run`]. + pub fn backdrop(mut self, backdrop: Backdrop) -> Self { + self.backdrop = Some(backdrop); + self + } + + /// Run a callback immediately before the main window closes. + pub fn on_exit(mut self, callback: impl FnOnce() + Send + 'static) -> Self { + self.on_exit = Some(Box::new(callback)); + self + } + + /// Run with custom WinUI setup; the caller manages windows and content. + pub fn run_custom(self, setup: F) -> Result<()> + where + F: FnOnce(&Application) -> Result<()> + Send + 'static, + { + let _bootstrap = init_app_platform()?; + + let setup = Mutex::new(Some(setup)); + let result_slot: Arc>> = Arc::new(Mutex::new(Ok(()))); + let result_slot_cb = Arc::clone(&result_slot); + let start_result = + Application::Start(&ApplicationInitializationCallback::new(move |_params| { + let inner = || -> Result<()> { + let setup = setup.lock().unwrap().take().unwrap(); + + let on_launched: Box Result<()>> = Box::new(move || { + let app = APP_SLOT.with(|slot| slot.borrow().clone()).unwrap(); + install_xaml_controls_resources(&app)?; + run_callback("setup", || setup(&app)) + }); + + let app = create_reactor_application(on_launched)?; + APP_SLOT.with(|slot| { + *slot.borrow_mut() = Some(app); + }); + Ok(()) + }; + if let Err(err) = inner() { + *result_slot_cb.lock().unwrap() = Err(err); + } + })); + + let result = + start_result.and_then(|_| std::mem::replace(&mut *result_slot.lock().unwrap(), Ok(()))); + report_app_start_result(result) + } + + /// Run the app, building the root component on the UI thread. + pub fn run(self, root_factory: F) -> Result<()> + where + F: FnOnce() -> C + Send + 'static, + C: Component + 'static, + { + let _bootstrap = init_app_platform()?; + + let title = self.title.unwrap_or_default(); + let eager = self.eager_templated_realization; + let size = self.inner_size; + let constraints = self.inner_constraints; + let presenter = self.presenter; + let backdrop = self.backdrop; + let on_exit = Arc::new(Mutex::new(self.on_exit)); + let factory = Mutex::new(Some(root_factory)); + let result_slot: Arc>> = Arc::new(Mutex::new(Ok(()))); + let result_slot_cb = Arc::clone(&result_slot); + let start_result = + Application::Start(&ApplicationInitializationCallback::new(move |_params| { + let on_exit = Arc::clone(&on_exit); + let inner = || -> Result<()> { + let factory = factory.lock().unwrap().take().unwrap(); + + let title = title.clone(); + let on_launched: Box Result<()>> = Box::new(move || { + let app = APP_SLOT.with(|slot| slot.borrow().clone()).unwrap(); + install_xaml_controls_resources(&app)?; + + run_callback("OnLaunched", move || { + let root: Box = Box::new(factory()); + let host = ReactorHost::new_with_window_options( + &title, + size, + constraints, + root, + |recon| { + recon.eager_templated_realization = eager; + }, + )?; + host.set_presenter(presenter); + if let Some(bd) = backdrop { + host.set_backdrop(bd); + } + host.activate()?; + // Exit the process on window close. Application.Exit() + // fail-fasts due to live COM refs, so terminate directly. + let on_exit = Arc::clone(&on_exit); + let _ = host + .window() + .add_Closed(move |_, _| { + if let Some(callback) = on_exit.lock().unwrap().take() { + callback(); + } + std::process::exit(0); + })? + .into_token(); + HOST_SLOT.with(|slot| { + *slot.borrow_mut() = Some(host); + }); + Ok(()) + }) + }); + + let app = create_reactor_application(on_launched)?; + APP_SLOT.with(|slot| { + *slot.borrow_mut() = Some(app); + }); + Ok(()) + }; + if let Err(err) = inner() { + *result_slot_cb.lock().unwrap() = Err(err); + } + })); + + let result = + start_result.and_then(|_| std::mem::replace(&mut *result_slot.lock().unwrap(), Ok(()))); + report_app_start_result(result) + } + + /// Convenience entry point that accepts a render function directly, + /// avoiding the empty-struct `Component` pattern. + /// + /// The render function can return any type that implements `Into` + /// — widget builders, `Element`, layout containers, etc. + /// + /// ```ignore + /// fn app(cx: &mut RenderCx) -> impl Into { + /// let (count, set_count) = cx.use_state(0); + /// button(format!("Clicks: {count}")) + /// .on_click(move || set_count.call(count + 1)) + /// } + /// + /// fn main() -> Result<()> { + /// App::new().render(app) + /// } + /// ``` + pub fn render(self, f: F) -> Result<()> + where + F: Fn(&mut crate::core::render_context::RenderCx) -> R + Send + 'static, + R: Into, + { + self.run(move || RenderFn(f)) + } +} + +/// Internal wrapper: adapts `Fn(&mut RenderCx) -> impl Into` into +/// `Component<()>` so it can be used with the existing host machinery. +struct RenderFn(F); + +impl crate::core::component::Component for RenderFn +where + F: Fn(&mut crate::core::render_context::RenderCx) -> R + 'static, + R: Into, +{ + fn render( + &self, + _props: &(), + cx: &mut crate::core::render_context::RenderCx, + ) -> crate::core::element::Element { + (self.0)(cx).into() + } +} + +fn run_callback(label: &'static str, f: F) -> Result<()> +where + F: FnOnce() -> Result<()>, +{ + match std::panic::catch_unwind(AssertUnwindSafe(f)) { + Ok(Ok(())) => Ok(()), + Ok(Err(err)) => { + crate::diagnostics::emit(&format!( + "windows_reactor: {label} callback returned error: {err:?}" + )); + Err(err) + } + Err(payload) => { + let msg = crate::diagnostics::format_panic_payload(&payload); + crate::diagnostics::emit(&format!( + "windows_reactor: {label} callback panicked: {msg}" + )); + Err(Error::new(E_FAIL, format!("{label} panicked: {msg}"))) + } + } +} + +fn report_app_start_result(result: Result<()>) -> Result<()> { + if let Err(err) = &result { + crate::diagnostics::emit(&format!( + "windows_reactor: Application::Start failed: {err:?}" + )); + } + result +} + +fn init_app_platform() -> Result { + crate::diagnostics::install(); + + // SAFETY: FFI call into user32; returns HRESULT and has no aliasing requirements. + unsafe { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2).ok()? }; + + // SAFETY: FFI call into ole32; null reserved arg is documented as required. + let coinit_hr = unsafe { CoInitializeEx(std::ptr::null(), COINIT_APARTMENTTHREADED as u32) }; + if coinit_hr == RPC_E_CHANGED_MODE { + return Err(Error::new( + RPC_E_CHANGED_MODE, + "windows_reactor::App: the calling thread is already initialized for COM in an \ + apartment incompatible with STA (likely MTA). WinUI 3 requires STA. \ + Call App::run / App::run_custom from `main` (or another thread you fully \ + own) and do not call CoInitializeEx(COINIT_MULTITHREADED) before this call.", + )); + } + coinit_hr.ok()?; + + BootstrapHandle::initialize() +} + +struct BootstrapHandle; + +impl BootstrapHandle { + fn initialize() -> Result { + unsafe { + MddBootstrapInitialize2( + WINDOWSAPPSDK_RELEASE_MAJORMINOR as u32, + WINDOWSAPPSDK_RELEASE_VERSION_TAG_W.as_ptr(), + PACKAGE_VERSION { + Anonymous: { + PACKAGE_VERSION_0 { + Version: WINDOWSAPPSDK_RUNTIME_VERSION_UINT64, + } + }, + }, + MddBootstrapInitializeOptions_OnNoMatch_ShowUI + | MddBootstrapInitializeOptions_OnPackageIdentity_NOOP, + ) + .ok()?; + + Ok(Self) + } + } +} + +impl Drop for BootstrapHandle { + fn drop(&mut self) { + unsafe { MddBootstrapShutdown() }; + } +} diff --git a/third_party/windows-winui/windows-reactor/src/app_shim.rs b/third_party/windows-winui/windows-reactor/src/app_shim.rs new file mode 100644 index 0000000000..6240b7915c --- /dev/null +++ b/third_party/windows-winui/windows-reactor/src/app_shim.rs @@ -0,0 +1,73 @@ +use std::cell::RefCell; +use windows_core::*; + +use super::bindings::*; + +implement_decl! { + impl ReactorApplicationOverrides as pub ReactorApplicationOverrides_Impl: [IApplicationOverrides, IXamlMetadataProvider] +} + +pub struct ReactorApplicationOverrides { + controls_provider: RefCell>, + on_launched: RefCell Result<()>>>>, +} + +impl ReactorApplicationOverrides { + fn new(on_launched: Box Result<()>>) -> Self { + Self { + controls_provider: RefCell::new(None), + on_launched: RefCell::new(Some(on_launched)), + } + } + + fn provider(&self) -> Result { + if let Some(p) = self.controls_provider.borrow().as_ref() { + return Ok(p.clone()); + } + let p = XamlControlsXamlMetaDataProvider::new()?; + *self.controls_provider.borrow_mut() = Some(p.clone()); + Ok(p) + } +} + +impl IApplicationOverrides_Impl for ReactorApplicationOverrides_Impl { + fn OnLaunched(&self, _args: windows_core::Ref) -> Result<()> { + if let Some(cb) = self.on_launched.borrow_mut().take() { + cb()?; + } + Ok(()) + } +} + +impl IXamlMetadataProvider_Impl for ReactorApplicationOverrides_Impl { + fn GetXamlType(&self, r#type: &TypeName) -> Result { + let provider: IXamlMetadataProvider = self.provider()?.cast()?; + provider.GetXamlType(r#type) + } + + fn GetXamlTypeByFullName(&self, full_name: &windows_core::HSTRING) -> Result { + let provider: IXamlMetadataProvider = self.provider()?.cast()?; + let full_name = full_name.to_string_lossy(); + provider.GetXamlTypeByFullName(&full_name) + } + + fn GetXmlnsDefinitions(&self) -> Result> { + let provider: IXamlMetadataProvider = self.provider()?.cast()?; + provider.GetXmlnsDefinitions() + } +} + +pub(crate) fn create_reactor_application( + on_launched: Box Result<()>>, +) -> Result { + Application::compose(ReactorApplicationOverrides::new(on_launched)) +} + +pub(crate) fn install_xaml_controls_resources(app: &Application) -> Result<()> { + let controls = XamlControlsResources::new()?; + let as_rd: ResourceDictionary = controls.cast()?; + let resources = app.get_Resources()?; + let merged = resources.get_MergedDictionaries()?; + merged.Append(&as_rd)?; + Ok(()) +} diff --git a/third_party/windows-winui/windows-reactor/src/bindings.rs b/third_party/windows-winui/windows-reactor/src/bindings.rs new file mode 100644 index 0000000000..81d6707420 --- /dev/null +++ b/third_party/windows-winui/windows-reactor/src/bindings.rs @@ -0,0 +1,26441 @@ +windows_core::link!("ole32.dll" "system" fn CoInitializeEx(pvreserved : *const core::ffi::c_void, dwcoinit : u32) -> windows_core::HRESULT); +windows_core::link!("user32.dll" "system" fn GetDpiForWindow(hwnd : HWND) -> u32); +windows_core::link!("user32.dll" "system" fn GetMonitorInfoW(hmonitor : HMONITOR, lpmi : *mut MONITORINFO) -> windows_core::BOOL); +windows_core::link!("microsoft.windowsappruntime.bootstrap.dll" "system" fn MddBootstrapInitialize2(majorminorversion : u32, versiontag : *const u16, minversion : PACKAGE_VERSION, options : MddBootstrapInitializeOptions) -> windows_core::HRESULT); +windows_core::link!("microsoft.windowsappruntime.bootstrap.dll" "system" fn MddBootstrapShutdown()); +windows_core::link!("user32.dll" "system" fn MonitorFromWindow(hwnd : HWND, dwflags : MONITOR_FROM_FLAGS) -> HMONITOR); +windows_core::link!("user32.dll" "system" fn PostMessageW(hwnd : HWND, msg : u32, wparam : WPARAM, lparam : LPARAM) -> windows_core::BOOL); +windows_core::link!("user32.dll" "system" fn SetProcessDpiAwarenessContext(value : DPI_AWARENESS_CONTEXT) -> windows_core::BOOL); +windows_core::link!("user32.dll" "system" fn SetWindowPos(hwnd : HWND, hwndinsertafter : HWND, x : i32, y : i32, cx : i32, cy : i32, uflags : SET_WINDOW_POS_FLAGS) -> windows_core::BOOL); +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AppBar(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!(AppBar, windows_core::IUnknown, windows_core::IInspectable); +windows_core::imp::required_hierarchy!( + AppBar, + ContentControl, + Control, + FrameworkElement, + UIElement, + DependencyObject +); +impl AppBar {} +impl windows_core::RuntimeType for AppBar { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for AppBar { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for AppBar { + type Target = IAppBar; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for AppBar { + const NAME: &'static str = "Microsoft.UI.Xaml.Controls.AppBar"; +} +unsafe impl Send for AppBar {} +unsafe impl Sync for AppBar {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AppBarButton(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + AppBarButton, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!( + AppBarButton, + ICommandBarElement, + Button, + ButtonBase, + ContentControl, + Control, + FrameworkElement, + UIElement, + DependencyObject +); +impl AppBarButton { + pub fn new() -> windows_core::Result { + Self::IAppBarButtonFactory(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateInstance)( + windows_core::Interface::as_raw(this), + core::ptr::null_mut(), + &mut core::ptr::null_mut(), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + pub fn compose(compose: T) -> windows_core::Result + where + T: windows_core::Compose, + { + Self::IAppBarButtonFactory(|this| unsafe { + let (derived__, base__) = windows_core::Compose::compose(compose); + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateInstance)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(&derived__), + base__ as *mut _ as _, + &mut result__, + ) + .ok()?; + let _ = &derived__; + windows_core::Type::from_abi(result__) + }) + } + fn IAppBarButtonFactory windows_core::Result>( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache = + windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for AppBarButton { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for AppBarButton { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for AppBarButton { + type Target = IAppBarButton; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for AppBarButton { + const NAME: &'static str = "Microsoft.UI.Xaml.Controls.AppBarButton"; +} +unsafe impl Send for AppBarButton {} +unsafe impl Sync for AppBarButton {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AppBarSeparator(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + AppBarSeparator, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!( + AppBarSeparator, + ICommandBarElement, + Control, + FrameworkElement, + UIElement, + DependencyObject +); +impl AppBarSeparator { + pub fn new() -> windows_core::Result { + Self::IAppBarSeparatorFactory(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateInstance)( + windows_core::Interface::as_raw(this), + core::ptr::null_mut(), + &mut core::ptr::null_mut(), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + pub fn compose(compose: T) -> windows_core::Result + where + T: windows_core::Compose, + { + Self::IAppBarSeparatorFactory(|this| unsafe { + let (derived__, base__) = windows_core::Compose::compose(compose); + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateInstance)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(&derived__), + base__ as *mut _ as _, + &mut result__, + ) + .ok()?; + let _ = &derived__; + windows_core::Type::from_abi(result__) + }) + } + fn IAppBarSeparatorFactory< + R, + F: FnOnce(&IAppBarSeparatorFactory) -> windows_core::Result, + >( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache = + windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for AppBarSeparator { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for AppBarSeparator { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for AppBarSeparator { + type Target = IAppBarSeparator; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for AppBarSeparator { + const NAME: &'static str = "Microsoft.UI.Xaml.Controls.AppBarSeparator"; +} +unsafe impl Send for AppBarSeparator {} +unsafe impl Sync for AppBarSeparator {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AppBarToggleButton(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + AppBarToggleButton, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!( + AppBarToggleButton, + ICommandBarElement, + ToggleButton, + ButtonBase, + ContentControl, + Control, + FrameworkElement, + UIElement, + DependencyObject +); +impl AppBarToggleButton { + pub fn new() -> windows_core::Result { + Self::IAppBarToggleButtonFactory(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateInstance)( + windows_core::Interface::as_raw(this), + core::ptr::null_mut(), + &mut core::ptr::null_mut(), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + pub fn compose(compose: T) -> windows_core::Result + where + T: windows_core::Compose, + { + Self::IAppBarToggleButtonFactory(|this| unsafe { + let (derived__, base__) = windows_core::Compose::compose(compose); + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateInstance)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(&derived__), + base__ as *mut _ as _, + &mut result__, + ) + .ok()?; + let _ = &derived__; + windows_core::Type::from_abi(result__) + }) + } + fn IAppBarToggleButtonFactory< + R, + F: FnOnce(&IAppBarToggleButtonFactory) -> windows_core::Result, + >( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache< + AppBarToggleButton, + IAppBarToggleButtonFactory, + > = windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for AppBarToggleButton { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for AppBarToggleButton { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for AppBarToggleButton { + type Target = IAppBarToggleButton; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for AppBarToggleButton { + const NAME: &'static str = "Microsoft.UI.Xaml.Controls.AppBarToggleButton"; +} +unsafe impl Send for AppBarToggleButton {} +unsafe impl Sync for AppBarToggleButton {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AppWindow(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + AppWindow, + windows_core::IUnknown, + windows_core::IInspectable +); +impl AppWindow {} +impl windows_core::RuntimeType for AppWindow { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for AppWindow { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for AppWindow { + type Target = IAppWindow; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for AppWindow { + const NAME: &'static str = "Microsoft.UI.Windowing.AppWindow"; +} +unsafe impl Send for AppWindow {} +unsafe impl Sync for AppWindow {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AppWindowPresenter(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + AppWindowPresenter, + windows_core::IUnknown, + windows_core::IInspectable +); +impl AppWindowPresenter {} +impl windows_core::RuntimeType for AppWindowPresenter { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for AppWindowPresenter { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for AppWindowPresenter { + type Target = IAppWindowPresenter; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for AppWindowPresenter { + const NAME: &'static str = "Microsoft.UI.Windowing.AppWindowPresenter"; +} +unsafe impl Send for AppWindowPresenter {} +unsafe impl Sync for AppWindowPresenter {} +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct AppWindowPresenterKind(pub i32); +impl AppWindowPresenterKind { + pub const Default: Self = Self(0i32); + pub const CompactOverlay: Self = Self(1i32); + pub const FullScreen: Self = Self(2i32); + pub const Overlapped: Self = Self(3i32); +} +impl windows_core::TypeKind for AppWindowPresenterKind { + type TypeKind = windows_core::CopyType; +} +impl windows_core::RuntimeType for AppWindowPresenterKind { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::from_slice( + b"enum(Microsoft.UI.Windowing.AppWindowPresenterKind;i4)", + ); +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AppWindowTitleBar(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + AppWindowTitleBar, + windows_core::IUnknown, + windows_core::IInspectable +); +impl AppWindowTitleBar {} +impl windows_core::RuntimeType for AppWindowTitleBar { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for AppWindowTitleBar { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for AppWindowTitleBar { + type Target = IAppWindowTitleBar; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for AppWindowTitleBar { + const NAME: &'static str = "Microsoft.UI.Windowing.AppWindowTitleBar"; +} +unsafe impl Send for AppWindowTitleBar {} +unsafe impl Sync for AppWindowTitleBar {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Application(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + Application, + windows_core::IUnknown, + windows_core::IInspectable +); +impl Application { + pub fn new() -> windows_core::Result { + Self::IApplicationFactory(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateInstance)( + windows_core::Interface::as_raw(this), + core::ptr::null_mut(), + &mut core::ptr::null_mut(), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + pub fn compose(compose: T) -> windows_core::Result + where + T: windows_core::Compose, + { + Self::IApplicationFactory(|this| unsafe { + let (derived__, base__) = windows_core::Compose::compose(compose); + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateInstance)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(&derived__), + base__ as *mut _ as _, + &mut result__, + ) + .ok()?; + let _ = &derived__; + windows_core::Type::from_abi(result__) + }) + } + pub fn get_Current() -> windows_core::Result { + Self::IApplicationStatics(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).get_Current)( + windows_core::Interface::as_raw(this), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + pub fn Start(callback: P0) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + Self::IApplicationStatics(|this| unsafe { + (windows_core::Interface::vtable(this).Start)( + windows_core::Interface::as_raw(this), + callback.param().abi(), + ) + .ok() + }) + } + fn IApplicationFactory windows_core::Result>( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache = + windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } + fn IApplicationStatics windows_core::Result>( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache = + windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for Application { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for Application { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for Application { + type Target = IApplication; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for Application { + const NAME: &'static str = "Microsoft.UI.Xaml.Application"; +} +unsafe impl Send for Application {} +unsafe impl Sync for Application {} +windows_core::imp::define_interface!( + ApplicationInitializationCallback, + ApplicationInitializationCallback_Vtbl, + 0xd8eef1c9_1234_56f1_9963_45dd9c80a661 +); +impl windows_core::RuntimeType for ApplicationInitializationCallback { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_interface::(); +} +impl ApplicationInitializationCallback { + pub fn new) + 'static>( + invoke: F, + ) -> Self { + let com = windows_core::imp::DelegateBox::::new( + &ApplicationInitializationCallbackBox::::VTABLE, + invoke, + ); + unsafe { core::mem::transmute(windows_core::imp::Box::new(com)) } + } +} +#[repr(C)] +#[doc(hidden)] +pub struct ApplicationInitializationCallback_Vtbl { + base__: windows_core::IUnknown_Vtbl, + Invoke: unsafe extern "system" fn( + this: *mut core::ffi::c_void, + p: *mut core::ffi::c_void, + ) -> windows_core::HRESULT, +} +struct ApplicationInitializationCallbackBox< + F: Fn(windows_core::Ref) + 'static, +>(core::marker::PhantomData<(fn() -> F,)>); +impl) + 'static> + ApplicationInitializationCallbackBox +{ + const VTABLE: ApplicationInitializationCallback_Vtbl = ApplicationInitializationCallback_Vtbl { + base__: + windows_core::IUnknown_Vtbl { + QueryInterface: windows_core::imp::DelegateBox::< + ApplicationInitializationCallback, + F, + >::QueryInterface, + AddRef: + windows_core::imp::DelegateBox::::AddRef, + Release: + windows_core::imp::DelegateBox::::Release, + }, + Invoke: Self::Invoke, + }; + unsafe extern "system" fn Invoke( + this: *mut core::ffi::c_void, + p: *mut core::ffi::c_void, + ) -> windows_core::HRESULT { + unsafe { + let this = &mut *(this as *mut *mut core::ffi::c_void + as *mut windows_core::imp::DelegateBox); + (this.invoke)(core::mem::transmute_copy(&p)); + windows_core::HRESULT(0) + } + } +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ApplicationInitializationCallbackParams(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + ApplicationInitializationCallbackParams, + windows_core::IUnknown, + windows_core::IInspectable +); +impl ApplicationInitializationCallbackParams {} +impl windows_core::RuntimeType for ApplicationInitializationCallbackParams { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::for_class::< + Self, + IApplicationInitializationCallbackParams, + >(); +} +unsafe impl windows_core::Interface for ApplicationInitializationCallbackParams { + type Vtable = ::Vtable; + const IID: windows_core::GUID = + ::IID; +} +impl core::ops::Deref for ApplicationInitializationCallbackParams { + type Target = IApplicationInitializationCallbackParams; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for ApplicationInitializationCallbackParams { + const NAME: &'static str = "Microsoft.UI.Xaml.ApplicationInitializationCallbackParams"; +} +unsafe impl Send for ApplicationInitializationCallbackParams {} +unsafe impl Sync for ApplicationInitializationCallbackParams {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AutoSuggestBox(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + AutoSuggestBox, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!( + AutoSuggestBox, + ItemsControl, + Control, + FrameworkElement, + UIElement, + DependencyObject +); +impl AutoSuggestBox { + 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< + AutoSuggestBox, + windows_core::imp::IGenericFactory, + > = windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for AutoSuggestBox { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for AutoSuggestBox { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for AutoSuggestBox { + type Target = IAutoSuggestBox; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for AutoSuggestBox { + const NAME: &'static str = "Microsoft.UI.Xaml.Controls.AutoSuggestBox"; +} +unsafe impl Send for AutoSuggestBox {} +unsafe impl Sync for AutoSuggestBox {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AutoSuggestBoxQuerySubmittedEventArgs(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + AutoSuggestBoxQuerySubmittedEventArgs, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!(AutoSuggestBoxQuerySubmittedEventArgs, DependencyObject); +impl AutoSuggestBoxQuerySubmittedEventArgs { + 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< + AutoSuggestBoxQuerySubmittedEventArgs, + windows_core::imp::IGenericFactory, + > = windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for AutoSuggestBoxQuerySubmittedEventArgs { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for AutoSuggestBoxQuerySubmittedEventArgs { + type Vtable = ::Vtable; + const IID: windows_core::GUID = + ::IID; +} +impl core::ops::Deref for AutoSuggestBoxQuerySubmittedEventArgs { + type Target = IAutoSuggestBoxQuerySubmittedEventArgs; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for AutoSuggestBoxQuerySubmittedEventArgs { + const NAME: &'static str = "Microsoft.UI.Xaml.Controls.AutoSuggestBoxQuerySubmittedEventArgs"; +} +unsafe impl Send for AutoSuggestBoxQuerySubmittedEventArgs {} +unsafe impl Sync for AutoSuggestBoxQuerySubmittedEventArgs {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AutoSuggestBoxSuggestionChosenEventArgs(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + AutoSuggestBoxSuggestionChosenEventArgs, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!(AutoSuggestBoxSuggestionChosenEventArgs, DependencyObject); +impl AutoSuggestBoxSuggestionChosenEventArgs { + 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< + AutoSuggestBoxSuggestionChosenEventArgs, + windows_core::imp::IGenericFactory, + > = windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for AutoSuggestBoxSuggestionChosenEventArgs { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::for_class::< + Self, + IAutoSuggestBoxSuggestionChosenEventArgs, + >(); +} +unsafe impl windows_core::Interface for AutoSuggestBoxSuggestionChosenEventArgs { + type Vtable = ::Vtable; + const IID: windows_core::GUID = + ::IID; +} +impl core::ops::Deref for AutoSuggestBoxSuggestionChosenEventArgs { + type Target = IAutoSuggestBoxSuggestionChosenEventArgs; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for AutoSuggestBoxSuggestionChosenEventArgs { + const NAME: &'static str = "Microsoft.UI.Xaml.Controls.AutoSuggestBoxSuggestionChosenEventArgs"; +} +unsafe impl Send for AutoSuggestBoxSuggestionChosenEventArgs {} +unsafe impl Sync for AutoSuggestBoxSuggestionChosenEventArgs {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AutoSuggestBoxTextChangedEventArgs(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + AutoSuggestBoxTextChangedEventArgs, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!(AutoSuggestBoxTextChangedEventArgs, DependencyObject); +impl AutoSuggestBoxTextChangedEventArgs { + 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< + AutoSuggestBoxTextChangedEventArgs, + windows_core::imp::IGenericFactory, + > = windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for AutoSuggestBoxTextChangedEventArgs { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for AutoSuggestBoxTextChangedEventArgs { + type Vtable = ::Vtable; + const IID: windows_core::GUID = + ::IID; +} +impl core::ops::Deref for AutoSuggestBoxTextChangedEventArgs { + type Target = IAutoSuggestBoxTextChangedEventArgs; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for AutoSuggestBoxTextChangedEventArgs { + const NAME: &'static str = "Microsoft.UI.Xaml.Controls.AutoSuggestBoxTextChangedEventArgs"; +} +unsafe impl Send for AutoSuggestBoxTextChangedEventArgs {} +unsafe impl Sync for AutoSuggestBoxTextChangedEventArgs {} +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct AutoSuggestionBoxTextChangeReason(pub i32); +impl AutoSuggestionBoxTextChangeReason { + pub const UserInput: Self = Self(0i32); + pub const ProgrammaticChange: Self = Self(1i32); + pub const SuggestionChosen: Self = Self(2i32); +} +impl windows_core::TypeKind for AutoSuggestionBoxTextChangeReason { + type TypeKind = windows_core::CopyType; +} +impl windows_core::RuntimeType for AutoSuggestionBoxTextChangeReason { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::from_slice( + b"enum(Microsoft.UI.Xaml.Controls.AutoSuggestionBoxTextChangeReason;i4)", + ); +} +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct AutomationHeadingLevel(pub i32); +impl AutomationHeadingLevel { + pub const None: Self = Self(0i32); + pub const Level1: Self = Self(1i32); + pub const Level2: Self = Self(2i32); + pub const Level3: Self = Self(3i32); + pub const Level4: Self = Self(4i32); + pub const Level5: Self = Self(5i32); + pub const Level6: Self = Self(6i32); + pub const Level7: Self = Self(7i32); + pub const Level8: Self = Self(8i32); + pub const Level9: Self = Self(9i32); +} +impl windows_core::TypeKind for AutomationHeadingLevel { + type TypeKind = windows_core::CopyType; +} +impl windows_core::RuntimeType for AutomationHeadingLevel { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::from_slice( + b"enum(Microsoft.UI.Xaml.Automation.Peers.AutomationHeadingLevel;i4)", + ); +} +#[repr(transparent)] +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct AutomationLiveSetting(pub i32); +impl AutomationLiveSetting { + pub const Off: Self = Self(0i32); + pub const Polite: Self = Self(1i32); + pub const Assertive: Self = Self(2i32); +} +impl windows_core::TypeKind for AutomationLiveSetting { + type TypeKind = windows_core::CopyType; +} +impl windows_core::RuntimeType for AutomationLiveSetting { + const SIGNATURE: windows_core::imp::ConstBuffer = windows_core::imp::ConstBuffer::from_slice( + b"enum(Microsoft.UI.Xaml.Automation.Peers.AutomationLiveSetting;i4)", + ); +} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AutomationPeer(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + AutomationPeer, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!(AutomationPeer, DependencyObject); +impl AutomationPeer {} +impl windows_core::RuntimeType for AutomationPeer { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for AutomationPeer { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for AutomationPeer { + type Target = IAutomationPeer; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for AutomationPeer { + const NAME: &'static str = "Microsoft.UI.Xaml.Automation.Peers.AutomationPeer"; +} +unsafe impl Send for AutomationPeer {} +unsafe impl Sync for AutomationPeer {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AutomationProperties(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + AutomationProperties, + windows_core::IUnknown, + windows_core::IInspectable +); +impl AutomationProperties { + pub fn SetAutomationId(element: P0, value: &str) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + Self::IAutomationPropertiesStatics(|this| unsafe { + (windows_core::Interface::vtable(this).SetAutomationId)( + windows_core::Interface::as_raw(this), + element.param().abi(), + core::mem::transmute_copy(&windows_core::HSTRING::from(value)), + ) + .ok() + }) + } + pub fn SetHelpText(element: P0, value: &str) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + Self::IAutomationPropertiesStatics(|this| unsafe { + (windows_core::Interface::vtable(this).SetHelpText)( + windows_core::Interface::as_raw(this), + element.param().abi(), + core::mem::transmute_copy(&windows_core::HSTRING::from(value)), + ) + .ok() + }) + } + pub fn SetName(element: P0, value: &str) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + Self::IAutomationPropertiesStatics(|this| unsafe { + (windows_core::Interface::vtable(this).SetName)( + windows_core::Interface::as_raw(this), + element.param().abi(), + core::mem::transmute_copy(&windows_core::HSTRING::from(value)), + ) + .ok() + }) + } + pub fn SetLiveSetting(element: P0, value: AutomationLiveSetting) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + Self::IAutomationPropertiesStatics(|this| unsafe { + (windows_core::Interface::vtable(this).SetLiveSetting)( + windows_core::Interface::as_raw(this), + element.param().abi(), + value, + ) + .ok() + }) + } + pub fn SetHeadingLevel( + element: P0, + value: AutomationHeadingLevel, + ) -> windows_core::Result<()> + where + P0: windows_core::Param, + { + Self::IAutomationPropertiesStatics(|this| unsafe { + (windows_core::Interface::vtable(this).SetHeadingLevel)( + windows_core::Interface::as_raw(this), + element.param().abi(), + value, + ) + .ok() + }) + } + fn IAutomationPropertiesStatics< + R, + F: FnOnce(&IAutomationPropertiesStatics) -> windows_core::Result, + >( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache< + AutomationProperties, + IAutomationPropertiesStatics, + > = windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for AutomationProperties { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for AutomationProperties { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for AutomationProperties { + type Target = IAutomationProperties; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for AutomationProperties { + const NAME: &'static str = "Microsoft.UI.Xaml.Automation.AutomationProperties"; +} +unsafe impl Send for AutomationProperties {} +unsafe impl Sync for AutomationProperties {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BitmapImage(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + BitmapImage, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!(BitmapImage, BitmapSource, ImageSource, DependencyObject); +impl BitmapImage { + 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< + BitmapImage, + windows_core::imp::IGenericFactory, + > = windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for BitmapImage { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for BitmapImage { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for BitmapImage { + type Target = IBitmapImage; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for BitmapImage { + const NAME: &'static str = "Microsoft.UI.Xaml.Media.Imaging.BitmapImage"; +} +unsafe impl Send for BitmapImage {} +unsafe impl Sync for BitmapImage {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BitmapSource(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + BitmapSource, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!(BitmapSource, ImageSource, DependencyObject); +impl BitmapSource {} +impl windows_core::RuntimeType for BitmapSource { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for BitmapSource { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for BitmapSource { + type Target = IBitmapSource; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for BitmapSource { + const NAME: &'static str = "Microsoft.UI.Xaml.Media.Imaging.BitmapSource"; +} +unsafe impl Send for BitmapSource {} +unsafe impl Sync for BitmapSource {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Block(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!(Block, windows_core::IUnknown, windows_core::IInspectable); +windows_core::imp::required_hierarchy!(Block, TextElement, DependencyObject); +impl Block {} +impl windows_core::RuntimeType for Block { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for Block { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for Block { + type Target = IBlock; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for Block { + const NAME: &'static str = "Microsoft.UI.Xaml.Documents.Block"; +} +unsafe impl Send for Block {} +unsafe impl Sync for Block {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BlockCollection(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + BlockCollection, + windows_core::IUnknown, + windows_core::IInspectable, + windows_collections::IVector +); +impl BlockCollection {} +impl windows_core::RuntimeType for BlockCollection { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::>(); +} +unsafe impl windows_core::Interface for BlockCollection { + type Vtable = as windows_core::Interface>::Vtable; + const IID: windows_core::GUID = + as windows_core::Interface>::IID; +} +impl core::ops::Deref for BlockCollection { + type Target = windows_collections::IVector; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for BlockCollection { + const NAME: &'static str = "Microsoft.UI.Xaml.Documents.BlockCollection"; +} +unsafe impl Send for BlockCollection {} +unsafe impl Sync for BlockCollection {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Border(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!(Border, windows_core::IUnknown, windows_core::IInspectable); +windows_core::imp::required_hierarchy!(Border, FrameworkElement, UIElement, DependencyObject); +impl Border { + 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 = + windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for Border { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for Border { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for Border { + type Target = IBorder; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for Border { + const NAME: &'static str = "Microsoft.UI.Xaml.Controls.Border"; +} +unsafe impl Send for Border {} +unsafe impl Sync for Border {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BreadcrumbBar(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!( + BreadcrumbBar, + windows_core::IUnknown, + windows_core::IInspectable +); +windows_core::imp::required_hierarchy!( + BreadcrumbBar, + Control, + FrameworkElement, + UIElement, + DependencyObject +); +impl BreadcrumbBar { + pub fn new() -> windows_core::Result { + Self::IBreadcrumbBarFactory(|this| unsafe { + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateInstance)( + windows_core::Interface::as_raw(this), + core::ptr::null_mut(), + &mut core::ptr::null_mut(), + &mut result__, + ) + .and_then(|| windows_core::Type::from_abi(result__)) + }) + } + pub fn compose(compose: T) -> windows_core::Result + where + T: windows_core::Compose, + { + Self::IBreadcrumbBarFactory(|this| unsafe { + let (derived__, base__) = windows_core::Compose::compose(compose); + let mut result__ = core::mem::zeroed(); + (windows_core::Interface::vtable(this).CreateInstance)( + windows_core::Interface::as_raw(this), + core::mem::transmute_copy(&derived__), + base__ as *mut _ as _, + &mut result__, + ) + .ok()?; + let _ = &derived__; + windows_core::Type::from_abi(result__) + }) + } + fn IBreadcrumbBarFactory windows_core::Result>( + callback: F, + ) -> windows_core::Result { + static SHARED: windows_core::imp::FactoryCache = + windows_core::imp::FactoryCache::new(); + SHARED.call(callback) + } +} +impl windows_core::RuntimeType for BreadcrumbBar { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for BreadcrumbBar { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for BreadcrumbBar { + type Target = IBreadcrumbBar; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for BreadcrumbBar { + const NAME: &'static str = "Microsoft.UI.Xaml.Controls.BreadcrumbBar"; +} +unsafe impl Send for BreadcrumbBar {} +unsafe impl Sync for BreadcrumbBar {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Brush(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!(Brush, windows_core::IUnknown, windows_core::IInspectable); +windows_core::imp::required_hierarchy!(Brush, DependencyObject); +impl Brush {} +impl windows_core::RuntimeType for Brush { + const SIGNATURE: windows_core::imp::ConstBuffer = + windows_core::imp::ConstBuffer::for_class::(); +} +unsafe impl windows_core::Interface for Brush { + type Vtable = ::Vtable; + const IID: windows_core::GUID = ::IID; +} +impl core::ops::Deref for Brush { + type Target = IBrush; + fn deref(&self) -> &Self::Target { + unsafe { core::mem::transmute(self) } + } +} +impl windows_core::RuntimeName for Brush { + const NAME: &'static str = "Microsoft.UI.Xaml.Media.Brush"; +} +unsafe impl Send for Brush {} +unsafe impl Sync for Brush {} +#[repr(transparent)] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Button(windows_core::IUnknown); +windows_core::imp::interface_hierarchy!(Button, windows_core::IUnknown, windows_core::IInspectable); +windows_core::imp::required_hierarchy!( + Button, + ButtonBase, + ContentControl, + Control, + FrameworkElement, + UIElement, + DependencyObject +); +impl Button { + pub fn new() -> windows_core::Result