.NET bindings to the Haiku API

I write implementations for basic items like entry_ref, but all the class interfaces are generated. That makes it possible for me to make decisions about internals and re-generate the bindings as needed, and it’s less prone to error as long as the API declarations I start with are reliable. My declarations are based on the Haiku .h files, with a lot of stuff weeded out and additional notation that Ocaml needs. (For example, each parameter needs to be marked if it isn’t really input but rather a pointer to a return value.) I fooled around a little Rust, and I the issues weren’t much different but don’t really remember.

I think it just about has to work that way, as opposed to a hand written wrapper for each function. That’s going to pile up to the point where it’s unmaintainable.

I’m experimenting with these wrappers as a proof of concept to see where it leads then I’ll think about how to generate them automatically.

Hi @trungnt2910 would you be able to add the private classes to the bindings? I was thinking all of them actually because in a way or another these might be needed. See the ColumnListView or the example below.
If I subclass a BButton tha app crashes beyond remedy when a B_MOUSE_IDLE is delivered. The cause should be that when the BView hits this code (C++):

case B_MOUSE_IDLE:
{
	BPoint where;
	if (message->FindPoint("be:view_where", &where) != B_OK)
		break;
		BToolTip* tip;
	if (GetToolTipAt(where, &tip))
		ShowToolTip(tip);
	else
		BHandler::MessageReceived(message);
	break;
}

BToolTip is not generated correctly in the bindings as it does not have a public constructor and other methods are missing, as far as I can tell.

EDIT: this happens only when I subclass, otherwise it won’t happen. It might be also due to GetToolTipAt() returns true while the tooltip is null regardless and this problem surfaces only when a derived class is involved. It happens with other classes as well like BOutlineListView.
As a temporary workaround I filter out the B_MOUSE_IDLE and I don’t forward it to base.MessageReceived().

Wait, what?

Private classes are private and should not be visible to anyone outside of libbe, not even the glue code. They only power the implementation behind the public classes (and potentially take up some bytes if used as fields, though that does not matter since the C# bindings do not fully mirror the structure).

That is sus. It looks like the bool return value has not been marshalled properly, although in the binding generated source files the correct attributes have already been applied:

        [SuppressUnmanagedCodeSecurity, UnmanagedFunctionPointer(__CallingConvention.Cdecl)]
        [return: MarshalAs(UnmanagedType.I1)]
        internal unsafe delegate bool Func_bool___IntPtr_Haiku_Interface_BPoint___Internal___IntPtr(__IntPtr __instance, global::Haiku.Interface.BPoint.__Internal arg1, __IntPtr arg2);

The important thing here is [return: MarshalAs(UnmanagedType.I1)], telling the CoreCLR to convert between .NET’s 4-byte bools to native 1-byte bools.

That is the C++ header, the managed class seems to be empty, no default constructor or even public members.
Probably CppSharp generated a dummy class like it did with the layout classes. Do you recall it?

Generally speaking I agree but the classes in shared, for example, are largely used in third-party apps. We also do it in Genio with the ColumnListView and PathMonitor :innocent:
They probably should go in a hypothetical BExperimental namespace instead of BPrivate. But this deserves a separate discussion.
The problem here is that the private headers do not imply they should not be visible.
BView::SetToolTip(BToolTip* tip) is public, if we hide or don’t generate BToolTip, we have an irreconcilable discrepancy in view of which CppSharp creates a dummy class.

I’ve checked that already and haven’t noticed anything suspicious.
I’m still minded that the problem is with BTooltip not being properly generated.
There is no issue with using BButton or whatever class directly, the tooltip is handled by libbe but when you subclass it then the problem surfaces.
Without a debugger, this is a pure speculation of course L.

Ahh, I did not notice that the header was in the private headers folder. These folders are not included in the generator, and I might fix that when I have time.

BToolTip does not seem to live in the BPrivate namespace so adding it might not be much of a problem.

Yes but again I would kindly ask you to include all private headers even the ones under BPrivate. They are freely accessible in C++ and should remain as such in C#. We could put them under Haiku.Private namespace but they should be available in any case.
It’s up to the devs to make a fair use of them like we do in C++ or do it at our own risk, at least.
Many thanks for your effort here, it is much appreciated!

libshared is a .a library, it will be statically linked.

This means you can use the functions in your third party application, but Haiku is free to remove those functions at any time since they are not part of the ABI, in essence you might need to redo some code for a newer Haiku version, but the already compiled version will keep working.

Whenever possible and makes sense (I.e. a self-contained class) we make a copy in Genio in the override folder so we keep source compatibility.
We keep it in sync with the Haiku source and when we encounter a bug, we fix it and contribute back.
With .NET, albeit technically feasible, this would be less than ideal. However, considering the current state of the .NET port, it’s a non-problem.
IMHO What we may do is marking these classes as experimental instead of private.
Private means “you must not use it, in any case outside of the Haiku source” while experimental means “you could at your own risk, it might change at any time without notice”. This would make the API even more generator-friendly.
Taking the BToolTip example, it should not be in the private header section as it is referenced by a public method in BView. It’s not much of a problem in C++ but with automatic generators it may lead to unexpected behaviours.

They are not. For example, BToolTip cannot be referenced from C++, since no public header is exposing the complete type. You would have to explicitly include the private headers.

It’s not really dummy; it still corresponds to an instance of BToolTip acquired somewhere else across the Haiku API surface. If the instance is somehow invalid, then that’s a bug of the generator.

I have tried including the private headers to the search path but that has failed due to dotnet-haiku relying on a master header and searching everything from there. Furthermore, as I said above, none of the public headers includes the private headers (for ToolTip.h, not even the private ones!).

For dotnet-haiku, I am also leaving all the risks to the end devs. Adding private classes means risking an ABI break between previous glue libraries and Haiku. If you know everything so well and need the private classes so badly, there’s still the option of subclassing the opaque wrapper classes and do pointer hacks in unsafe blocks.

The best thing in my opinion in this case though is to request the Haiku developers to expose these classes to the public. Then dotnet-haiku can follow up in a update.