diff --git a/src/common/windows/winrt.cpp b/src/common/windows/winrt.cpp index 14ec54f4d..767fa626e 100644 --- a/src/common/windows/winrt.cpp +++ b/src/common/windows/winrt.cpp @@ -4,119 +4,110 @@ #include -// A COM vtable is a flat array of function pointers: IUnknown's three methods, IInspectable's three, -// then the interface's own methods in declaration order. The pair below writes that array by hand -// instead of deriving from winrt::impl::abi<...>. -// -// Deriving is the idiomatic choice, and it would let the compiler check every slot -- but it cannot -// be used here. A class with virtual functions makes clang emit the vtable of its abstract base, and -// each pure slot of that table refers to libc++abi's __cxa_pure_virtual. fastfetch is linked by the C -// compiler and therefore has neither libc++ nor libc++abi, so that reference would be the only -// unresolved symbol in the program. A hand-written table has no virtual functions at all, which -// keeps this file as free of the C++ runtime as `media_windows.cpp` is. +#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. namespace { -using WinrtIterableAbi = winrt::impl::abi>::type; +using WinrtIterableAbi = abi_t>; +using WinrtIteratorAbi = abi_t>; +using WinrtHstringAbiOut = winrt::impl::arg_out; struct FFWinrtHstringIterable; struct FFWinrtHstringIterator; -struct FFWinrtHstringIterableVtbl { - std::int32_t (STDMETHODCALLTYPE* QueryInterface)(FFWinrtHstringIterable* self, GUID const& riid, void** result); - std::uint32_t (STDMETHODCALLTYPE* AddRef)(FFWinrtHstringIterable* self); - std::uint32_t (STDMETHODCALLTYPE* Release)(FFWinrtHstringIterable* self); - std::int32_t (STDMETHODCALLTYPE* GetIids)(FFWinrtHstringIterable* self, std::uint32_t* count, GUID** iids); - std::int32_t (STDMETHODCALLTYPE* GetRuntimeClassName)(FFWinrtHstringIterable* self, void** name); - std::int32_t (STDMETHODCALLTYPE* GetTrustLevel)(FFWinrtHstringIterable* self, std::int32_t* level); - std::int32_t (STDMETHODCALLTYPE* First)(FFWinrtHstringIterable* self, void** result); -}; - -struct FFWinrtHstringIteratorVtbl { - std::int32_t (STDMETHODCALLTYPE* QueryInterface)(FFWinrtHstringIterator* self, GUID const& riid, void** result); - std::uint32_t (STDMETHODCALLTYPE* AddRef)(FFWinrtHstringIterator* self); - std::uint32_t (STDMETHODCALLTYPE* Release)(FFWinrtHstringIterator* self); - std::int32_t (STDMETHODCALLTYPE* GetIids)(FFWinrtHstringIterator* self, std::uint32_t* count, GUID** iids); - std::int32_t (STDMETHODCALLTYPE* GetRuntimeClassName)(FFWinrtHstringIterator* self, void** name); - std::int32_t (STDMETHODCALLTYPE* GetTrustLevel)(FFWinrtHstringIterator* self, std::int32_t* level); - std::int32_t (STDMETHODCALLTYPE* get_Current)(FFWinrtHstringIterator* self, HSTRING* value); - std::int32_t (STDMETHODCALLTYPE* get_HasCurrent)(FFWinrtHstringIterator* self, bool* value); - std::int32_t (STDMETHODCALLTYPE* MoveNext)(FFWinrtHstringIterator* self, bool* value); - std::int32_t (STDMETHODCALLTYPE* GetMany)(FFWinrtHstringIterator* self, std::uint32_t capacity, HSTRING* values, std::uint32_t* actual); -}; - -// Everything the pair allocates comes from malloc(), because the placement is driven by the C -// allocator rather than by `new` (see the note above). -struct FFWinrtHstringIterable { - const FFWinrtHstringIterableVtbl* lpVtbl; +struct FFWinrtHstringIterable final : WinrtIterableAbi { std::uint32_t refCount; FFlist strings; // HSTRING + + 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; }; -struct FFWinrtHstringIterator { - const FFWinrtHstringIteratorVtbl* lpVtbl; +struct FFWinrtHstringIterator final : WinrtIteratorAbi { std::uint32_t refCount; FFWinrtHstringIterable* owner; std::uint32_t index; + + 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; }; // --------------------------------------------------------------------------------------------- // IIterable // --------------------------------------------------------------------------------------------- -std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterable_QueryInterface(FFWinrtHstringIterable* self, GUID const& riid, void** result) { +std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::QueryInterface(winrt::guid const& riid, void** result) noexcept { if (!result) { return E_POINTER; } *result = nullptr; - if (IsEqualGUID(riid, winrt::guid_of()) || - IsEqualGUID(riid, winrt::guid_of()) || - IsEqualGUID(riid, winrt::guid_of>())) { - *result = self; - self->lpVtbl->AddRef(self); + if (riid == winrt::guid_of() || + riid == winrt::guid_of() || + riid == winrt::guid_of>()) { + *result = this; + AddRef(); return S_OK; } return E_NOINTERFACE; } -std::uint32_t STDMETHODCALLTYPE ffWinrtHstringIterable_AddRef(FFWinrtHstringIterable* self) { - return ++self->refCount; +std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterable::AddRef() noexcept { + return ++refCount; } -std::uint32_t STDMETHODCALLTYPE ffWinrtHstringIterable_Release(FFWinrtHstringIterable* self) { - std::uint32_t remaining = --self->refCount; +std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterable::Release() noexcept { + std::uint32_t remaining = --refCount; if (remaining == 0) { - FF_LIST_FOR_EACH(HSTRING, string, self->strings) { + FF_LIST_FOR_EACH(HSTRING, string, strings) { WindowsDeleteString(*string); } - ffListDestroy(&self->strings); - free(self); + ffListDestroy(&strings); + this->~FFWinrtHstringIterable(); + free(this); } return remaining; } -std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterable_GetIids(FFWinrtHstringIterable* self, std::uint32_t* count, GUID** iids) { - (void) self; +std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::GetIids(std::uint32_t* count, winrt::guid** iids) noexcept { *count = 0; *iids = nullptr; return S_OK; } -std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterable_GetRuntimeClassName(FFWinrtHstringIterable* self, void** name) { - (void) self; +std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::GetRuntimeClassName(void** name) noexcept { *name = nullptr; return S_OK; } // `level` is the ABI's 32-bit TrustLevel enum; BaseTrust is 0. -std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterable_GetTrustLevel(FFWinrtHstringIterable* self, std::int32_t* level) { - (void) self; - *level = 0; +std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::GetTrustLevel(winrt::Windows::Foundation::TrustLevel* level) noexcept { + *level = winrt::Windows::Foundation::TrustLevel::BaseTrust; return S_OK; } -std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterable_First(FFWinrtHstringIterable* self, void** result) { +std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::First(void** result) noexcept { if (!result) { return E_POINTER; } @@ -127,119 +118,104 @@ std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterable_First(FFWinrtHstringIterab return E_OUTOFMEMORY; } - iterator->lpVtbl = nullptr; // assigned below, once the table is declared + ::new (iterator) FFWinrtHstringIterator(); iterator->refCount = 1; - iterator->owner = self; + iterator->owner = this; iterator->index = 0; // The iterator outlives the call that created it, so it holds a reference of its own. - self->lpVtbl->AddRef(self); - - extern const FFWinrtHstringIteratorVtbl ffWinrtHstringIteratorVtbl; - iterator->lpVtbl = &ffWinrtHstringIteratorVtbl; + AddRef(); *result = iterator; return S_OK; } -const FFWinrtHstringIterableVtbl ffWinrtHstringIterableVtbl = { - ffWinrtHstringIterable_QueryInterface, - ffWinrtHstringIterable_AddRef, - ffWinrtHstringIterable_Release, - ffWinrtHstringIterable_GetIids, - ffWinrtHstringIterable_GetRuntimeClassName, - ffWinrtHstringIterable_GetTrustLevel, - ffWinrtHstringIterable_First, -}; - // --------------------------------------------------------------------------------------------- // IIterator // --------------------------------------------------------------------------------------------- -std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_QueryInterface(FFWinrtHstringIterator* self, GUID const& riid, void** result) { +std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::QueryInterface(winrt::guid const& riid, void** result) noexcept { if (!result) { return E_POINTER; } *result = nullptr; - if (IsEqualGUID(riid, winrt::guid_of()) || - IsEqualGUID(riid, winrt::guid_of()) || - IsEqualGUID(riid, winrt::guid_of>())) { - *result = self; - self->lpVtbl->AddRef(self); + if (riid == winrt::guid_of() || + riid == winrt::guid_of() || + riid == winrt::guid_of>()) { + *result = this; + AddRef(); return S_OK; } return E_NOINTERFACE; } -std::uint32_t STDMETHODCALLTYPE ffWinrtHstringIterator_AddRef(FFWinrtHstringIterator* self) { - return ++self->refCount; +std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterator::AddRef() noexcept { + return ++refCount; } -std::uint32_t STDMETHODCALLTYPE ffWinrtHstringIterator_Release(FFWinrtHstringIterator* self) { - std::uint32_t remaining = --self->refCount; +std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterator::Release() noexcept { + std::uint32_t remaining = --refCount; if (remaining == 0) { - self->owner->lpVtbl->Release(self->owner); - free(self); + owner->Release(); + this->~FFWinrtHstringIterator(); + free(this); } return remaining; } -std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_GetIids(FFWinrtHstringIterator* self, std::uint32_t* count, GUID** iids) { - (void) self; +std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetIids(std::uint32_t* count, winrt::guid** iids) noexcept { *count = 0; *iids = nullptr; return S_OK; } -std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_GetRuntimeClassName(FFWinrtHstringIterator* self, void** name) { - (void) self; +std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetRuntimeClassName(void** name) noexcept { *name = nullptr; return S_OK; } -std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_GetTrustLevel(FFWinrtHstringIterator* self, std::int32_t* level) { - (void) self; - *level = 0; +std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetTrustLevel(winrt::Windows::Foundation::TrustLevel* level) noexcept { + *level = winrt::Windows::Foundation::TrustLevel::BaseTrust; return S_OK; } -std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_get_Current(FFWinrtHstringIterator* self, HSTRING* value) { +std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::get_Current(WinrtHstringAbiOut value) noexcept { if (!value) { return E_POINTER; } *value = nullptr; - if (self->index >= self->owner->strings.length) { + if (index >= owner->strings.length) { return E_BOUNDS; } // The caller owns what it gets, so hand out a duplicate rather than the stored string. - return WindowsDuplicateString(*(HSTRING*) ffListGet(&self->owner->strings, sizeof(HSTRING), self->index), value); + return WindowsDuplicateString(*(HSTRING*) ffListGet(&owner->strings, sizeof(HSTRING), index), reinterpret_cast(value)); } -std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_get_HasCurrent(FFWinrtHstringIterator* self, bool* value) { +std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::get_HasCurrent(bool* value) noexcept { if (!value) { return E_POINTER; } - *value = self->index < self->owner->strings.length; + *value = index < owner->strings.length; return S_OK; } -std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_MoveNext(FFWinrtHstringIterator* self, bool* value) { +std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::MoveNext(bool* value) noexcept { if (!value) { return E_POINTER; } - if (self->index < self->owner->strings.length) { - ++self->index; + if (index < owner->strings.length) { + ++index; } - *value = self->index < self->owner->strings.length; + *value = index < owner->strings.length; return S_OK; } -std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_GetMany(FFWinrtHstringIterator* self, std::uint32_t capacity, HSTRING* values, std::uint32_t* actual) { +std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetMany(std::uint32_t capacity, WinrtHstringAbiOut values, std::uint32_t* actual) noexcept { if (!actual) { return E_POINTER; } @@ -249,47 +225,22 @@ std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_GetMany(FFWinrtHstringIter return E_POINTER; } - std::uint32_t available = self->owner->strings.length - self->index; + std::uint32_t available = owner->strings.length - index; std::uint32_t taken = capacity < available ? capacity : available; + HSTRING* hstrings = reinterpret_cast(values); for (std::uint32_t i = 0; i < taken; ++i) { - WindowsDuplicateString(*(HSTRING*) ffListGet(&self->owner->strings, sizeof(HSTRING), self->index + i), &values[i]); + WindowsDuplicateString(*(HSTRING*) ffListGet(&owner->strings, sizeof(HSTRING), index + i), &hstrings[i]); } - self->index += taken; + index += taken; *actual = taken; return taken == capacity ? S_OK : S_FALSE; } -const FFWinrtHstringIteratorVtbl ffWinrtHstringIteratorVtbl = { - ffWinrtHstringIterator_QueryInterface, - ffWinrtHstringIterator_AddRef, - ffWinrtHstringIterator_Release, - ffWinrtHstringIterator_GetIids, - ffWinrtHstringIterator_GetRuntimeClassName, - ffWinrtHstringIterator_GetTrustLevel, - ffWinrtHstringIterator_get_Current, - ffWinrtHstringIterator_get_HasCurrent, - ffWinrtHstringIterator_MoveNext, - ffWinrtHstringIterator_GetMany, -}; - } // namespace -void ffDeleteHstring(HSTRING* value) { - if (*value) { - WindowsDeleteString(*value); - *value = nullptr; - } -} - -void ffStrbufSetHstring(FFstrbuf* destination, HSTRING value) { - uint32_t length = 0; - const wchar_t* raw = WindowsGetStringRawBuffer(value, &length); - ffStrbufSetNWS(destination, length, raw); -} - HRESULT ffWinrtCreateHstringIterable(const wchar_t* const* items, uint32_t count, abi_t>** result) { *result = nullptr; @@ -302,7 +253,7 @@ HRESULT ffWinrtCreateHstringIterable(const wchar_t* const* items, uint32_t count return E_OUTOFMEMORY; } - iterable->lpVtbl = &ffWinrtHstringIterableVtbl; + ::new (iterable) FFWinrtHstringIterable(); iterable->refCount = 1; ffListInitA(&iterable->strings, sizeof(HSTRING), count > 0 ? count : FF_LIST_DEFAULT_ALLOC); @@ -310,12 +261,12 @@ HRESULT ffWinrtCreateHstringIterable(const wchar_t* const* items, uint32_t count HSTRING* slot = (HSTRING*) ffListAdd(&iterable->strings, sizeof(HSTRING)); HRESULT hr = WindowsCreateString(items[i], (UINT32) ::wcslen(items[i]), slot); if (FAILED(hr)) { - iterable->lpVtbl->Release(iterable); + iterable->Release(); return hr; } } - *result = reinterpret_cast(iterable); + *result = static_cast(iterable); return S_OK; } diff --git a/src/common/windows/winrt.hpp b/src/common/windows/winrt.hpp index 46924f267..876a6d0d2 100644 --- a/src/common/windows/winrt.hpp +++ b/src/common/windows/winrt.hpp @@ -23,10 +23,19 @@ extern "C" { using winrt::impl::abi_t; // Frees an HSTRING and clears the pointer, so it can be used with [[gnu::cleanup]]. -void ffDeleteHstring(HSTRING* value); +static inline void ffDeleteHstring(HSTRING* value) { + if (*value) { + WindowsDeleteString(*value); + *value = nullptr; + } +} // Copies an HSTRING into an FFstrbuf, transcoding from UTF-16 to UTF-8. -void ffStrbufSetHstring(FFstrbuf* destination, HSTRING value); +static inline void ffStrbufSetHstring(FFstrbuf* destination, HSTRING value) { + uint32_t length = 0; + const wchar_t* raw = WindowsGetStringRawBuffer(value, &length); + ffStrbufSetNWS(destination, length, raw); +} // Wraps `items` in an IVector and hands out its IIterable view, which is the // parameter shape the FindAllAsync overloads taking additional properties expect. The caller owns