master ohmu/fastfetch-openbsd / src / common / windows / winrt.cpp
ohmu  ·  2026-10-15
  1#include "winrt.hpp"
  2
  3#if FF_HAVE_WINRT
  4
  5    #include <wchar.h>
  6
  7#if defined(__GNUC__) || defined(__clang__)
  8extern "C" [[noreturn]] void __cxa_pure_virtual(void) {
  9    __builtin_trap();
 10}
 11#endif
 12
 13// C++/WinRT supplies the ABI vtable shape, including IUnknown and IInspectable. These concrete
 14// classes only provide the implementation-specific state and methods; placement new keeps their
 15// storage under the C allocator used by the rest of fastfetch.
 16namespace {
 17
 18using WinrtIterableAbi = abi_t<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>;
 19using WinrtIteratorAbi = abi_t<winrt::Windows::Foundation::Collections::IIterator<winrt::hstring>>;
 20using WinrtHstringAbiOut = winrt::impl::arg_out<winrt::hstring>;
 21
 22struct FFWinrtHstringIterable;
 23struct FFWinrtHstringIterator;
 24
 25struct FFWinrtHstringIterable final : WinrtIterableAbi {
 26    std::uint32_t refCount;
 27    FFlist strings; // HSTRING
 28
 29    std::int32_t STDMETHODCALLTYPE QueryInterface(winrt::guid const& riid, void** result) noexcept override;
 30    std::uint32_t STDMETHODCALLTYPE AddRef() noexcept override;
 31    std::uint32_t STDMETHODCALLTYPE Release() noexcept override;
 32    std::int32_t STDMETHODCALLTYPE GetIids(std::uint32_t* count, winrt::guid** iids) noexcept override;
 33    std::int32_t STDMETHODCALLTYPE GetRuntimeClassName(void** name) noexcept override;
 34    std::int32_t STDMETHODCALLTYPE GetTrustLevel(winrt::Windows::Foundation::TrustLevel* level) noexcept override;
 35    std::int32_t STDMETHODCALLTYPE First(void** result) noexcept override;
 36};
 37
 38struct FFWinrtHstringIterator final : WinrtIteratorAbi {
 39    std::uint32_t refCount;
 40    FFWinrtHstringIterable* owner;
 41    std::uint32_t index;
 42
 43    std::int32_t STDMETHODCALLTYPE QueryInterface(winrt::guid const& riid, void** result) noexcept override;
 44    std::uint32_t STDMETHODCALLTYPE AddRef() noexcept override;
 45    std::uint32_t STDMETHODCALLTYPE Release() noexcept override;
 46    std::int32_t STDMETHODCALLTYPE GetIids(std::uint32_t* count, winrt::guid** iids) noexcept override;
 47    std::int32_t STDMETHODCALLTYPE GetRuntimeClassName(void** name) noexcept override;
 48    std::int32_t STDMETHODCALLTYPE GetTrustLevel(winrt::Windows::Foundation::TrustLevel* level) noexcept override;
 49    std::int32_t STDMETHODCALLTYPE get_Current(WinrtHstringAbiOut value) noexcept override;
 50    std::int32_t STDMETHODCALLTYPE get_HasCurrent(bool* value) noexcept override;
 51    std::int32_t STDMETHODCALLTYPE MoveNext(bool* value) noexcept override;
 52    std::int32_t STDMETHODCALLTYPE GetMany(std::uint32_t capacity, WinrtHstringAbiOut values, std::uint32_t* actual) noexcept override;
 53};
 54
 55// ---------------------------------------------------------------------------------------------
 56// IIterable<HSTRING>
 57// ---------------------------------------------------------------------------------------------
 58
 59std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::QueryInterface(winrt::guid const& riid, void** result) noexcept {
 60    if (!result) {
 61        return E_POINTER;
 62    }
 63    *result = nullptr;
 64
 65    if (riid == winrt::guid_of<winrt::Windows::Foundation::IUnknown>() ||
 66        riid == winrt::guid_of<winrt::Windows::Foundation::IInspectable>() ||
 67        riid == winrt::guid_of<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>()) {
 68        *result = this;
 69        AddRef();
 70        return S_OK;
 71    }
 72
 73    return E_NOINTERFACE;
 74}
 75
 76std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterable::AddRef() noexcept {
 77    return ++refCount;
 78}
 79
 80std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterable::Release() noexcept {
 81    std::uint32_t remaining = --refCount;
 82    if (remaining == 0) {
 83        FF_LIST_FOR_EACH(HSTRING, string, strings) {
 84            WindowsDeleteString(*string);
 85        }
 86        ffListDestroy(&strings);
 87        this->~FFWinrtHstringIterable();
 88        free(this);
 89    }
 90    return remaining;
 91}
 92
 93std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::GetIids(std::uint32_t* count, winrt::guid** iids) noexcept {
 94    *count = 0;
 95    *iids = nullptr;
 96    return S_OK;
 97}
 98
 99std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::GetRuntimeClassName(void** name) noexcept {
100    *name = nullptr;
101    return S_OK;
102}
103
104// `level` is the ABI's 32-bit TrustLevel enum; BaseTrust is 0.
105std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::GetTrustLevel(winrt::Windows::Foundation::TrustLevel* level) noexcept {
106    *level = winrt::Windows::Foundation::TrustLevel::BaseTrust;
107    return S_OK;
108}
109
110std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::First(void** result) noexcept {
111    if (!result) {
112        return E_POINTER;
113    }
114    *result = nullptr;
115
116    auto* iterator = (FFWinrtHstringIterator*) malloc(sizeof(FFWinrtHstringIterator));
117    if (!iterator) {
118        return E_OUTOFMEMORY;
119    }
120
121    ::new (iterator) FFWinrtHstringIterator();
122    iterator->refCount = 1;
123    iterator->owner = this;
124    iterator->index = 0;
125
126    // The iterator outlives the call that created it, so it holds a reference of its own.
127    AddRef();
128
129    *result = iterator;
130    return S_OK;
131}
132
133// ---------------------------------------------------------------------------------------------
134// IIterator<HSTRING>
135// ---------------------------------------------------------------------------------------------
136
137std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::QueryInterface(winrt::guid const& riid, void** result) noexcept {
138    if (!result) {
139        return E_POINTER;
140    }
141    *result = nullptr;
142
143    if (riid == winrt::guid_of<winrt::Windows::Foundation::IUnknown>() ||
144        riid == winrt::guid_of<winrt::Windows::Foundation::IInspectable>() ||
145        riid == winrt::guid_of<winrt::Windows::Foundation::Collections::IIterator<winrt::hstring>>()) {
146        *result = this;
147        AddRef();
148        return S_OK;
149    }
150
151    return E_NOINTERFACE;
152}
153
154std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterator::AddRef() noexcept {
155    return ++refCount;
156}
157
158std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterator::Release() noexcept {
159    std::uint32_t remaining = --refCount;
160    if (remaining == 0) {
161        owner->Release();
162        this->~FFWinrtHstringIterator();
163        free(this);
164    }
165    return remaining;
166}
167
168std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetIids(std::uint32_t* count, winrt::guid** iids) noexcept {
169    *count = 0;
170    *iids = nullptr;
171    return S_OK;
172}
173
174std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetRuntimeClassName(void** name) noexcept {
175    *name = nullptr;
176    return S_OK;
177}
178
179std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetTrustLevel(winrt::Windows::Foundation::TrustLevel* level) noexcept {
180    *level = winrt::Windows::Foundation::TrustLevel::BaseTrust;
181    return S_OK;
182}
183
184std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::get_Current(WinrtHstringAbiOut value) noexcept {
185    if (!value) {
186        return E_POINTER;
187    }
188    *value = nullptr;
189
190    if (index >= owner->strings.length) {
191        return E_BOUNDS;
192    }
193
194    // The caller owns what it gets, so hand out a duplicate rather than the stored string.
195    return WindowsDuplicateString(*(HSTRING*) ffListGet(&owner->strings, sizeof(HSTRING), index), reinterpret_cast<HSTRING*>(value));
196}
197
198std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::get_HasCurrent(bool* value) noexcept {
199    if (!value) {
200        return E_POINTER;
201    }
202    *value = index < owner->strings.length;
203    return S_OK;
204}
205
206std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::MoveNext(bool* value) noexcept {
207    if (!value) {
208        return E_POINTER;
209    }
210
211    if (index < owner->strings.length) {
212        ++index;
213    }
214    *value = index < owner->strings.length;
215    return S_OK;
216}
217
218std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetMany(std::uint32_t capacity, WinrtHstringAbiOut values, std::uint32_t* actual) noexcept {
219    if (!actual) {
220        return E_POINTER;
221    }
222    *actual = 0;
223
224    if (capacity && !values) {
225        return E_POINTER;
226    }
227
228    std::uint32_t available = owner->strings.length - index;
229    std::uint32_t taken = capacity < available ? capacity : available;
230    HSTRING* hstrings = reinterpret_cast<HSTRING*>(values);
231
232    for (std::uint32_t i = 0; i < taken; ++i) {
233        HRESULT hr = WindowsDuplicateString(*(HSTRING*) ffListGet(&owner->strings, sizeof(HSTRING), index + i), &hstrings[i]);
234        if (FAILED(hr)) {
235            // Whatever was handed out already belongs to the caller, so a half filled array has to
236            // be taken back before the error goes out: the caller has no way to free a partial one.
237            for (std::uint32_t j = 0; j < i; ++j) {
238                WindowsDeleteString(hstrings[j]);
239            }
240            return hr;
241        }
242    }
243
244    index += taken;
245    *actual = taken;
246
247    return taken == capacity ? S_OK : S_FALSE;
248}
249
250} // namespace
251
252HRESULT ffWinrtCreateHstringIterable(const wchar_t* const* items, uint32_t count, abi_t<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>** result) {
253    *result = nullptr;
254
255    if (count > 0 && !items) {
256        return E_INVALIDARG;
257    }
258
259    auto* iterable = (FFWinrtHstringIterable*) malloc(sizeof(FFWinrtHstringIterable));
260    if (!iterable) {
261        return E_OUTOFMEMORY;
262    }
263
264    ::new (iterable) FFWinrtHstringIterable();
265    iterable->refCount = 1;
266    ffListInitA(&iterable->strings, sizeof(HSTRING), count > 0 ? count : FF_LIST_DEFAULT_ALLOC);
267
268    for (uint32_t i = 0; i < count; ++i) {
269        HSTRING* slot = (HSTRING*) ffListAdd(&iterable->strings, sizeof(HSTRING));
270        // ffListAdd does not zero the slot it hands out, and Release() walks every one of them, so
271        // the slot has to hold a usable HSTRING before WindowsCreateString gets a chance to fail.
272        *slot = nullptr;
273        HRESULT hr = WindowsCreateString(items[i], (UINT32) ::wcslen(items[i]), slot);
274        if (FAILED(hr)) {
275            iterable->Release();
276            return hr;
277        }
278    }
279
280    *result = static_cast<WinrtIterableAbi*>(iterable);
281    return S_OK;
282}
283
284#endif // FF_HAVE_WINRT