[GSOC 2023] .NET Port

Latest builds are failing with this error:

  [  7%] Building CXX object inc/CMakeFiles/corguids.dir/__/pal/prebuilt/idl/cordebug_i.cpp.o
  In file included from /home/runner/work/dotnet-builds/dotnet-builds/runtime/src/coreclr/pal/inc/rt/palrt.h:630,
                   from /home/runner/work/dotnet-builds/dotnet-builds/runtime/src/coreclr/pal/inc/rt/rpc.h:15,
                   from /home/runner/work/dotnet-builds/dotnet-builds/runtime/src/coreclr/pal/prebuilt/idl/cordebug_i.cpp:29:
  /home/runner/work/dotnet-builds/dotnet-builds/runtime/src/coreclr/pal/inc/rt/safecrt.h:1093:16: error: conflicting declaration of C function 'size_t wcsnlen(const WCHAR*, size_t)'
   1093 | size_t __cdecl wcsnlen(const WCHAR *inString, size_t inMaxSize);
        |                ^~~~~~~
  In file included from /home/runner/work/dotnet-builds/dotnet-builds/dotnet-rootfs/boot/system/develop/headers/posix/wctype.h:10,
                   from /home/runner/work/dotnet-builds/dotnet-builds/runtime/src/coreclr/pal/inc/pal.h:51,
                   from /home/runner/work/dotnet-builds/dotnet-builds/runtime/src/coreclr/pal/inc/rt/palrt.h:136:
  /home/runner/work/dotnet-builds/dotnet-builds/dotnet-rootfs/boot/system/develop/headers/posix/wchar.h:122:17: note: previous declaration 'size_t wcsnlen(const wchar_t*, size_t)'
    122 | extern size_t   wcsnlen(const wchar_t *wcs, size_t maxLength);
        |                 ^~~~~~~
  /home/runner/work/dotnet-builds/dotnet-builds/runtime/src/coreclr/pal/inc/rt/safecrt.h:1098:16: error: conflicting declaration of C function 'size_t wcsnlen(const WCHAR*, size_t)'
   1098 | size_t __cdecl wcsnlen(const WCHAR *inString, size_t inMaxSize)
        |                ^~~~~~~
  /home/runner/work/dotnet-builds/dotnet-builds/dotnet-rootfs/boot/system/develop/headers/posix/wchar.h:122:17: note: previous declaration 'size_t wcsnlen(const wchar_t*, size_t)'
    122 | extern size_t   wcsnlen(const wchar_t *wcs, size_t maxLength);
        |                 ^~~~~~~
  make[2]: *** [inc/CMakeFiles/corguids.dir/build.make:76: inc/CMakeFiles/corguids.dir/__/pal/prebuilt/idl/cordebug_i.cpp.o] Error 1
  make[1]: *** [CMakeFiles/Makefile2:3250: inc/CMakeFiles/corguids.dir/all] Error 2
  make[1]: *** Waiting for unfinished jobs....

Seems like whoever included wctype.h did not expect wchar.h to be included as well.

This is the minimally reproducible example of the issue:

#include <stdint.h>
#include <stddef.h>
#include <wctype.h>

extern "C"
{
typedef char16_t WCHAR;
#define __cdecl

size_t __cdecl wcsnlen(const WCHAR *inString, size_t inMaxSize)
{
    size_t n;

    /* Note that we do not check if s == nullptr, because we do not
     * return errno_t...
     */

    for (n = 0; n < inMaxSize && *inString; n++, inString++)
        ;

    return n;
}


}

int main()
{
    auto x = L"Test";
    return wcsnlen((const WCHAR*)x, 3) - 3;
}

Compiling on Linux and Haiku gives different results:

trung@DESKTOP-5OCA2N2:~$ g++ test.cpp -o test
trung@DESKTOP-5OCA2N2:~$ haiku_loader g++ test.cpp -o test
test.cpp:49:16: error: conflicting declaration of C function 'size_t wcsnlen(const WCHAR*, size_t)'
   49 | size_t __cdecl wcsnlen(const WCHAR *inString, size_t inMaxSize)
      |                ^~~~~~~
In file included from /boot/system/develop/headers/posix/wctype.h:10,
                 from test.cpp:42:
/boot/system/develop/headers/posix/wchar.h:122:17: note: previous declaration 'size_t wcsnlen(const wchar_t*, size_t)'
  122 | extern size_t   wcsnlen(const wchar_t *wcs, size_t maxLength);
      |                 ^~~~~~~
test.cpp: In function 'int main()':
test.cpp:69:20: error: cannot convert 'const WCHAR*' {aka 'const char16_t*'} to 'const wchar_t*'
   69 |     return wcsnlen((const WCHAR*)x, 3) - 3;
      |                    ^~~~~~~~~~~~~~~
      |                    |
      |                    const WCHAR* {aka const char16_t*}
/boot/system/develop/headers/posix/wchar.h:122:40: note:   initializing argument 1 of 'size_t wcsnlen(const wchar_t*, size_t)'
  122 | extern size_t   wcsnlen(const wchar_t *wcs, size_t maxLength);
      |                         ~~~~~~~~~~~~~~~^~~
trung@DESKTOP-5OCA2N2:~$

Is it possible to run applications that use Avalonia GUI framework? For example sourcegit.

Theoretically yes if we could port Avalonia to use the Haiku API.

Though I can see a lot of trouble here: The Haiku API for GUI is multi-threaded, while all .NET UI frameworks I know of (WPF, WinUI, MAUI, etc.) handle UI-related events in a single thread.

It is possible to send messages from window looper so some global looper and also it is possible to draw on BView from another thread if do some trickery. Such technology is already used in Wine port, Blackbox CB etc…

It would be easier to get Maui or Xamarin Forms to work and directly use the BeAPI. The latter is more familiar to me, but Maui works in a similar way.

I believe Avalonia needs Skia to render. It does have a VNC backend though. So, I guess you could do what you say and have a BView render the UI entirely by taking the drawing operations and applying them and sending back the absolute mouse interactions.

Why everyone are so afraid of Skia? It can be compiled on Haiku easily and run fine. It is already used in Qt WebEngine port.

I plan to move app_server rendering from AGG to Skia for hardware rendering integration. Skia allows to transparently handle CPU and GPU buffers.

4 Likes

Nope.

Getting MAUI building for a new platform would involve tons and tons of manual work.

An example in the .NET ecosystem is GTK. The Uno Platform has had support for GTK for years via a Skia buffer and a few thousand lines of platform-specific code for handling input. MAUI, on the other hand, has yet to see any complete port for GTK.

I’m not a fan of UNO because last time I tried it is had fairly half baked Mac support for Apple Silicon. I love Avalonia a lot more, but their mobile support is a joke. They have no way to support stack based UI (which most mobile platforms use) unless you make your own implementation. When they sort that out it will be usable.

Xamarin Forms is really easy to port. You basically create renderers in a platform specific assembly and register them with the framework. You can actually get it up with hardly any code if you return a default renderer wherever one is missing. I haven’t looked at Maui in depth, but the Handler idiom is the same concept.

The fundamental difference is native vs custom drawn UI. Avalonia and UNO both have target where the UI is just common UI rendered with a drawing API on a canvas. Whilst this is great for speed of porting, it gives you non native UI. It also gives you issues revolving around then creating a native theme. I kind of like the idea of native UI more.

The Avalonia Platform for Xamarin Forms was a POC I started. I took someone else’s port to an older version of Avalonia (0.8 iirc) and ported it to 11.0 (a lot had changed) and got it working on Mac and Windows. I was then making it run under Android. The benefit there was to iron out all the weirdness Avalonia has over mobile vs desktop. And someone was lamenting at how much effort our port to maul was. I thought it would be fun to have one codebase working on many platforms. But without the hassle of how Xamarin did it.

Skia is amazing. I use it all the time. I have even ported horrible hacks WPF canvas ui we had at work to it.

I wasn’t aware we had a working version so that makes many things a lot easier.

I even managed to run Skia with Vulkan hardware acceleration and my RadeonGfx driver.

4 Likes

Yes. The concept is similar.

However, for MAUI the handler and the shared code is statically tied, or at least that was the case when I messed with MAUI for GTK a few years ago. To get the most basic things building you would need some kind of automated stub generator.

Would be even more interesting to see the speed difference of Skia verses AGG.

It might be possible to look at even replacing AGG with Skia, since Skia has a more permissive license.

It doesn’t, AGG 2.5 is in GPL but no one uses that. all the existing forks of AGG (including the one in Haiku) are based on the 2.4 version, since 2.5 had little more than a relicensing. The few bugfixes that were in it have been reimplemented independently.

There are other reasons to consider replacing AGG, but this isn’t one…

1 Like

Well it still doesn’t mean that it isn’t worth looking at or experimenting with especially to see how Skia performs against using AGG in the context of app_server rendering.

Such an experiment would be very interesting to see if it is possible or worth it once we have some numbers, if @X512 would give it a try if they want to.