2026-09-23 13:33:13 +08:00
|
|
|
#include "winrt.hpp"
|
|
|
|
|
|
|
|
|
|
#if FF_HAVE_WINRT
|
|
|
|
|
|
|
|
|
|
#include <wchar.h>
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
|
|
|
extern "C" [[noreturn]] void __cxa_pure_virtual(void) {
|
|
|
|
|
__builtin_trap();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// C++/WinRT supplies the ABI vtable shape, including IUnknown and IInspectable. These concrete
|
|
|
|
|
// classes only provide the implementation-specific state and methods; placement new keeps their
|
|
|
|
|
// storage under the C allocator used by the rest of fastfetch.
|
2026-09-23 13:33:13 +08:00
|
|
|
namespace {
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
using WinrtIterableAbi = abi_t<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>;
|
|
|
|
|
using WinrtIteratorAbi = abi_t<winrt::Windows::Foundation::Collections::IIterator<winrt::hstring>>;
|
|
|
|
|
using WinrtHstringAbiOut = winrt::impl::arg_out<winrt::hstring>;
|
2026-09-23 13:33:13 +08:00
|
|
|
|
|
|
|
|
struct FFWinrtHstringIterable;
|
|
|
|
|
struct FFWinrtHstringIterator;
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
struct FFWinrtHstringIterable final : WinrtIterableAbi {
|
2026-09-23 13:33:13 +08:00
|
|
|
std::uint32_t refCount;
|
|
|
|
|
FFlist strings; // HSTRING
|
2026-09-24 09:41:16 +08:00
|
|
|
|
|
|
|
|
std::int32_t STDMETHODCALLTYPE QueryInterface(winrt::guid const& riid, void** result) noexcept override;
|
|
|
|
|
std::uint32_t STDMETHODCALLTYPE AddRef() noexcept override;
|
|
|
|
|
std::uint32_t STDMETHODCALLTYPE Release() noexcept override;
|
|
|
|
|
std::int32_t STDMETHODCALLTYPE GetIids(std::uint32_t* count, winrt::guid** iids) noexcept override;
|
|
|
|
|
std::int32_t STDMETHODCALLTYPE GetRuntimeClassName(void** name) noexcept override;
|
|
|
|
|
std::int32_t STDMETHODCALLTYPE GetTrustLevel(winrt::Windows::Foundation::TrustLevel* level) noexcept override;
|
|
|
|
|
std::int32_t STDMETHODCALLTYPE First(void** result) noexcept override;
|
2026-09-23 13:33:13 +08:00
|
|
|
};
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
struct FFWinrtHstringIterator final : WinrtIteratorAbi {
|
2026-09-23 13:33:13 +08:00
|
|
|
std::uint32_t refCount;
|
|
|
|
|
FFWinrtHstringIterable* owner;
|
|
|
|
|
std::uint32_t index;
|
2026-09-24 09:41:16 +08:00
|
|
|
|
|
|
|
|
std::int32_t STDMETHODCALLTYPE QueryInterface(winrt::guid const& riid, void** result) noexcept override;
|
|
|
|
|
std::uint32_t STDMETHODCALLTYPE AddRef() noexcept override;
|
|
|
|
|
std::uint32_t STDMETHODCALLTYPE Release() noexcept override;
|
|
|
|
|
std::int32_t STDMETHODCALLTYPE GetIids(std::uint32_t* count, winrt::guid** iids) noexcept override;
|
|
|
|
|
std::int32_t STDMETHODCALLTYPE GetRuntimeClassName(void** name) noexcept override;
|
|
|
|
|
std::int32_t STDMETHODCALLTYPE GetTrustLevel(winrt::Windows::Foundation::TrustLevel* level) noexcept override;
|
|
|
|
|
std::int32_t STDMETHODCALLTYPE get_Current(WinrtHstringAbiOut value) noexcept override;
|
|
|
|
|
std::int32_t STDMETHODCALLTYPE get_HasCurrent(bool* value) noexcept override;
|
|
|
|
|
std::int32_t STDMETHODCALLTYPE MoveNext(bool* value) noexcept override;
|
|
|
|
|
std::int32_t STDMETHODCALLTYPE GetMany(std::uint32_t capacity, WinrtHstringAbiOut values, std::uint32_t* actual) noexcept override;
|
2026-09-23 13:33:13 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
|
|
|
// IIterable<HSTRING>
|
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::QueryInterface(winrt::guid const& riid, void** result) noexcept {
|
2026-09-23 13:33:13 +08:00
|
|
|
if (!result) {
|
|
|
|
|
return E_POINTER;
|
|
|
|
|
}
|
|
|
|
|
*result = nullptr;
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
if (riid == winrt::guid_of<winrt::Windows::Foundation::IUnknown>() ||
|
|
|
|
|
riid == winrt::guid_of<winrt::Windows::Foundation::IInspectable>() ||
|
|
|
|
|
riid == winrt::guid_of<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>()) {
|
|
|
|
|
*result = this;
|
|
|
|
|
AddRef();
|
2026-09-23 13:33:13 +08:00
|
|
|
return S_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterable::AddRef() noexcept {
|
|
|
|
|
return ++refCount;
|
2026-09-23 13:33:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterable::Release() noexcept {
|
|
|
|
|
std::uint32_t remaining = --refCount;
|
2026-09-23 13:33:13 +08:00
|
|
|
if (remaining == 0) {
|
2026-09-24 09:41:16 +08:00
|
|
|
FF_LIST_FOR_EACH(HSTRING, string, strings) {
|
2026-09-23 13:33:13 +08:00
|
|
|
WindowsDeleteString(*string);
|
|
|
|
|
}
|
2026-09-24 09:41:16 +08:00
|
|
|
ffListDestroy(&strings);
|
|
|
|
|
this->~FFWinrtHstringIterable();
|
|
|
|
|
free(this);
|
2026-09-23 13:33:13 +08:00
|
|
|
}
|
|
|
|
|
return remaining;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::GetIids(std::uint32_t* count, winrt::guid** iids) noexcept {
|
2026-09-23 13:33:13 +08:00
|
|
|
*count = 0;
|
|
|
|
|
*iids = nullptr;
|
|
|
|
|
return S_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::GetRuntimeClassName(void** name) noexcept {
|
2026-09-23 13:33:13 +08:00
|
|
|
*name = nullptr;
|
|
|
|
|
return S_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// `level` is the ABI's 32-bit TrustLevel enum; BaseTrust is 0.
|
2026-09-24 09:41:16 +08:00
|
|
|
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::GetTrustLevel(winrt::Windows::Foundation::TrustLevel* level) noexcept {
|
|
|
|
|
*level = winrt::Windows::Foundation::TrustLevel::BaseTrust;
|
2026-09-23 13:33:13 +08:00
|
|
|
return S_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::First(void** result) noexcept {
|
2026-09-23 13:33:13 +08:00
|
|
|
if (!result) {
|
|
|
|
|
return E_POINTER;
|
|
|
|
|
}
|
|
|
|
|
*result = nullptr;
|
|
|
|
|
|
|
|
|
|
auto* iterator = (FFWinrtHstringIterator*) malloc(sizeof(FFWinrtHstringIterator));
|
|
|
|
|
if (!iterator) {
|
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
::new (iterator) FFWinrtHstringIterator();
|
2026-09-23 13:33:13 +08:00
|
|
|
iterator->refCount = 1;
|
2026-09-24 09:41:16 +08:00
|
|
|
iterator->owner = this;
|
2026-09-23 13:33:13 +08:00
|
|
|
iterator->index = 0;
|
|
|
|
|
|
|
|
|
|
// The iterator outlives the call that created it, so it holds a reference of its own.
|
2026-09-24 09:41:16 +08:00
|
|
|
AddRef();
|
2026-09-23 13:33:13 +08:00
|
|
|
|
|
|
|
|
*result = iterator;
|
|
|
|
|
return S_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
|
|
|
// IIterator<HSTRING>
|
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::QueryInterface(winrt::guid const& riid, void** result) noexcept {
|
2026-09-23 13:33:13 +08:00
|
|
|
if (!result) {
|
|
|
|
|
return E_POINTER;
|
|
|
|
|
}
|
|
|
|
|
*result = nullptr;
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
if (riid == winrt::guid_of<winrt::Windows::Foundation::IUnknown>() ||
|
|
|
|
|
riid == winrt::guid_of<winrt::Windows::Foundation::IInspectable>() ||
|
|
|
|
|
riid == winrt::guid_of<winrt::Windows::Foundation::Collections::IIterator<winrt::hstring>>()) {
|
|
|
|
|
*result = this;
|
|
|
|
|
AddRef();
|
2026-09-23 13:33:13 +08:00
|
|
|
return S_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterator::AddRef() noexcept {
|
|
|
|
|
return ++refCount;
|
2026-09-23 13:33:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterator::Release() noexcept {
|
|
|
|
|
std::uint32_t remaining = --refCount;
|
2026-09-23 13:33:13 +08:00
|
|
|
if (remaining == 0) {
|
2026-09-24 09:41:16 +08:00
|
|
|
owner->Release();
|
|
|
|
|
this->~FFWinrtHstringIterator();
|
|
|
|
|
free(this);
|
2026-09-23 13:33:13 +08:00
|
|
|
}
|
|
|
|
|
return remaining;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetIids(std::uint32_t* count, winrt::guid** iids) noexcept {
|
2026-09-23 13:33:13 +08:00
|
|
|
*count = 0;
|
|
|
|
|
*iids = nullptr;
|
|
|
|
|
return S_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetRuntimeClassName(void** name) noexcept {
|
2026-09-23 13:33:13 +08:00
|
|
|
*name = nullptr;
|
|
|
|
|
return S_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetTrustLevel(winrt::Windows::Foundation::TrustLevel* level) noexcept {
|
|
|
|
|
*level = winrt::Windows::Foundation::TrustLevel::BaseTrust;
|
2026-09-23 13:33:13 +08:00
|
|
|
return S_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::get_Current(WinrtHstringAbiOut value) noexcept {
|
2026-09-23 13:33:13 +08:00
|
|
|
if (!value) {
|
|
|
|
|
return E_POINTER;
|
|
|
|
|
}
|
|
|
|
|
*value = nullptr;
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
if (index >= owner->strings.length) {
|
2026-09-23 13:33:13 +08:00
|
|
|
return E_BOUNDS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The caller owns what it gets, so hand out a duplicate rather than the stored string.
|
2026-09-24 09:41:16 +08:00
|
|
|
return WindowsDuplicateString(*(HSTRING*) ffListGet(&owner->strings, sizeof(HSTRING), index), reinterpret_cast<HSTRING*>(value));
|
2026-09-23 13:33:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::get_HasCurrent(bool* value) noexcept {
|
2026-09-23 13:33:13 +08:00
|
|
|
if (!value) {
|
|
|
|
|
return E_POINTER;
|
|
|
|
|
}
|
2026-09-24 09:41:16 +08:00
|
|
|
*value = index < owner->strings.length;
|
2026-09-23 13:33:13 +08:00
|
|
|
return S_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::MoveNext(bool* value) noexcept {
|
2026-09-23 13:33:13 +08:00
|
|
|
if (!value) {
|
|
|
|
|
return E_POINTER;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
if (index < owner->strings.length) {
|
|
|
|
|
++index;
|
2026-09-23 13:33:13 +08:00
|
|
|
}
|
2026-09-24 09:41:16 +08:00
|
|
|
*value = index < owner->strings.length;
|
2026-09-23 13:33:13 +08:00
|
|
|
return S_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetMany(std::uint32_t capacity, WinrtHstringAbiOut values, std::uint32_t* actual) noexcept {
|
2026-09-23 13:33:13 +08:00
|
|
|
if (!actual) {
|
|
|
|
|
return E_POINTER;
|
|
|
|
|
}
|
|
|
|
|
*actual = 0;
|
|
|
|
|
|
|
|
|
|
if (capacity && !values) {
|
|
|
|
|
return E_POINTER;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
std::uint32_t available = owner->strings.length - index;
|
2026-09-23 13:33:13 +08:00
|
|
|
std::uint32_t taken = capacity < available ? capacity : available;
|
2026-09-24 09:41:16 +08:00
|
|
|
HSTRING* hstrings = reinterpret_cast<HSTRING*>(values);
|
2026-09-23 13:33:13 +08:00
|
|
|
|
|
|
|
|
for (std::uint32_t i = 0; i < taken; ++i) {
|
2026-09-24 21:21:02 +08:00
|
|
|
HRESULT hr = WindowsDuplicateString(*(HSTRING*) ffListGet(&owner->strings, sizeof(HSTRING), index + i), &hstrings[i]);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
// Whatever was handed out already belongs to the caller, so a half filled array has to
|
|
|
|
|
// be taken back before the error goes out: the caller has no way to free a partial one.
|
|
|
|
|
for (std::uint32_t j = 0; j < i; ++j) {
|
|
|
|
|
WindowsDeleteString(hstrings[j]);
|
|
|
|
|
}
|
|
|
|
|
return hr;
|
|
|
|
|
}
|
2026-09-23 13:33:13 +08:00
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
index += taken;
|
2026-09-23 13:33:13 +08:00
|
|
|
*actual = taken;
|
|
|
|
|
|
|
|
|
|
return taken == capacity ? S_OK : S_FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
HRESULT ffWinrtCreateHstringIterable(const wchar_t* const* items, uint32_t count, abi_t<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>** result) {
|
|
|
|
|
*result = nullptr;
|
|
|
|
|
|
|
|
|
|
if (count > 0 && !items) {
|
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto* iterable = (FFWinrtHstringIterable*) malloc(sizeof(FFWinrtHstringIterable));
|
|
|
|
|
if (!iterable) {
|
|
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
::new (iterable) FFWinrtHstringIterable();
|
2026-09-23 13:33:13 +08:00
|
|
|
iterable->refCount = 1;
|
|
|
|
|
ffListInitA(&iterable->strings, sizeof(HSTRING), count > 0 ? count : FF_LIST_DEFAULT_ALLOC);
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
|
|
|
HSTRING* slot = (HSTRING*) ffListAdd(&iterable->strings, sizeof(HSTRING));
|
2026-09-24 21:21:02 +08:00
|
|
|
// ffListAdd does not zero the slot it hands out, and Release() walks every one of them, so
|
|
|
|
|
// the slot has to hold a usable HSTRING before WindowsCreateString gets a chance to fail.
|
|
|
|
|
*slot = nullptr;
|
2026-09-23 13:33:13 +08:00
|
|
|
HRESULT hr = WindowsCreateString(items[i], (UINT32) ::wcslen(items[i]), slot);
|
|
|
|
|
if (FAILED(hr)) {
|
2026-09-24 09:41:16 +08:00
|
|
|
iterable->Release();
|
2026-09-23 13:33:13 +08:00
|
|
|
return hr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-24 09:41:16 +08:00
|
|
|
*result = static_cast<WinrtIterableAbi*>(iterable);
|
2026-09-23 13:33:13 +08:00
|
|
|
return S_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // FF_HAVE_WINRT
|