My guess is that Pipewire is written in C because the author likes C (or at least somewhat dislikes C++). And it has "abstraction"... in the form of hand-baked vtables and observers created out of nested macros, void*, and intrusive linked lists.
An advantage of C is that it comes with a stable ABI, where Rust is unstable and C++ is a subject of conflict (though you can achieve a stable ABI by restricting your external API to C functions, and if you want cross-shared-object virtual calls like Pipewire, you might have to write the same pile of macros as PipeWire, which you were trying to save yourself from). The downsides are memory and type unsafety (surprisingly the only crash I've seen so far is a null pointer dereference, and I haven't hit any obvious memory or type errors), and a complete absence of "vocabulary" or templated types (they've created their own slotmap-like ID allocator but without a generation count resulting in ever-present "logical use-after-frees" in library and application code, and an intrusive linked list which they use even when hashmaps might provide somewhat faster iteration and asymptotically faster search).
I assumed the C ABI, and thus the cobbled-up vtables. Where the author might have used C++ to advantage would be automating other, internal details of the code. There is nothing quite like bugs you don't have because you didn't need to write the code where they would have been, or because you could run code at compile time to generate or at least verify your cobbled-together apparatus.
The saving grace is that it can at any time be switched over to be built with a C++ compiler, and then start to be modernized incrementally.
What you say is true. Unfortunately I don't think you can trivially switch it to a C++ compiler, because they use {[FOO] = ...} initialization.
It would be cool (not necessarily useful, probably not a good use of time) to rewrite it in Rust while keeping the dependency count in the single digits. It would be helpful to contributors to add architectural documentation.
C++20 added dot initialization, which has been supported in MSVC/GCC/Clang for over a year now. No released version of the C++ standard supports array index initialization, and jcelerier says Clang but not GCC supports it as an extension.
An advantage of C is that it comes with a stable ABI, where Rust is unstable and C++ is a subject of conflict (though you can achieve a stable ABI by restricting your external API to C functions, and if you want cross-shared-object virtual calls like Pipewire, you might have to write the same pile of macros as PipeWire, which you were trying to save yourself from). The downsides are memory and type unsafety (surprisingly the only crash I've seen so far is a null pointer dereference, and I haven't hit any obvious memory or type errors), and a complete absence of "vocabulary" or templated types (they've created their own slotmap-like ID allocator but without a generation count resulting in ever-present "logical use-after-frees" in library and application code, and an intrusive linked list which they use even when hashmaps might provide somewhat faster iteration and asymptotically faster search).