TGUI
TGUI copied to clipboard
Is there a C binding?
SFML has a C binding that facilitates the binding to other languages. Is there also a C binding for TGUI or a plan for implementing it?
There at least used to be one, which can be found at https://github.com/texus/CTGUI The master branch is stuck at TGUI 0.8 and hasn't been updated for several years.
Two months ago, I started some cleaning up with the intention to resurrect the library, which can be seen in the dev branch. This is however far from complete, and porting the backend system is proving to be difficult so I'm not sure if I'll finish it. I've been attempting to write a pure C backend using CSFML, which would also mean that there would be an ability to write custom backends in C, but I'm probably going to give this up and just implment the CSFML backend in C++ with a C wrapper around it.
The old 0.8 binding didn't have backends yet and TGUI just used SFML internally everywhere. This was however also very problematic, as both the TGUI and the CSFML libraries depended on SFML (causing issues with linking statically), and CTGUI had to depend on internal CSFML classes (which were just copied into the CTGUI project and would cause crashes if these files were updated in CSFML). So the fact that I can now build TGUI without a dependency to SFML, and implement the entire backend in CTGUI, is both a blessing (for fixing all the old issues) and a curse (for having to implement the entire backend in CTGUI instead of just wrapping some code from TGUI).
Thanks for the info.
Does it make sense to use the C binding as a base for a new binding in its current state? Or would it be lost work?
Right now the Gui class and anything related to it is still missing in the dev branch, and some new features are still missing, so you can't write a full binding based on the current code yet, but you could already write part of the binding.
Having someone that plans to use the C binding at least gives me some motivation to finish it. Next week I'll have a lot of spare time, so I'll probably have another attempt at implementing the gui class (although I'm giving up on the possibility to allow custom backends in bindings, people will just have to use the SFML backend or any other backend that I might implement in the future).
One big issue that I had with the old .Net binding was how to manage the memory. If the objects in your binding contain data (e.g. callback functions for signals) then somehow they need to stored. TGUI generally allows creating a widget, adding it to the gui, and then retrieving the widget from the gui later. This caused issues in the binding as TGUI only stores the c++ stuff and not the binding data. The way I solved it in the old .Net binding was to put part of the logic in C#: the C# container would store a list of C# widgets so that they wouldn't be garbage collected. This design ended up with a ton of issues though.
My plan to solve the memory management issues was to let the binding store a global map from C pointers to binding-specific data. I already prepared CTGUI to always return the same C pointer for the same widget (which used to not even be the case). I still need to add a callback when the widget is destroyed, so the binding knows that it can destroy the global data associated with that pointer. At least that's the current untested plan. If you are serious about writing a binding for TGUI then we might need to discuss the details based on the needs in your binding.
What language are you planning to write a binding for?
I maintain an Ada binding to SFML, and it was relatively easy to implement, mainly because it's a thin layer and CSFML has a simple interface. The only drawback is that memory is not automatically managed, it requires using the same create and destroy functions as the original. Only functions having primitive types which are very uncomfortable to use directly (like strings) have been wrapped to be used from Ada. I've thought also to have a thicker binding providing automatic memory management, but haven't had enough time and motivation to do it, for the moment.
I wanted to know if binding to TGUI would be as relatively easy as to CSFML, because it would be nice to have as complement to the SFML binding, and to provide another GUI possibility to Ada. If it required much thought time, then I might not have the time and energy to fulfill it, though.
I imagine it will be a bit more difficult than CSFML, but it's hard to predict how difficult.
The main difficulty compared to SFML is object lifetime. With SFML, you create a Font and Text object in Ada, and the font object needs to exist while rendering the text. Now imagine if you could create an Ada Font object, pass it to the Ada Text object and then were allowed to destroyed the font without issues. The C Text object still kept a reference to the C Font object internally. At a later time you could call something like text.getFont() which should create a new Ada font object based on the stored C font. That's the kind of situations you can get with TGUI, so it requires thinking about how you store things in the binding.
Luckily you probably shouldn't need to store many things in Ada, in my own .Net binding each C# object mostly just contained a C pointer which was used to forward all function calls to. There was only one major exception that I can remember, which started all the misery that I had with the .Net binding: it is not possible to get a pointer to a C# function, pass that pointer to C and letting the C code execute the C# callback function when an event occurs. Is there a similar restriction in Ada?
Callbacks needed to be stored in delegate objects, which needed to be stored somewhere. I stored them in the Widget objects, but those got garbage collected because C# has no idea how long they were supposed to exist. Which lead to even more complicated code that introduced other unfixable problems. With a redesign of how the C binding manages widgets, I'm hoping that this time there is a solution to all these problems, but the only way to know for certain is by writing a new binding.
There was only one major exception that I can remember, which started all the misery that I had with the .Net binding: it is not possible to get a pointer to a C# function, pass that pointer to C and letting the C code execute the C# callback function when an event occurs. Is there a similar restriction in Ada?
No, it's possible to get a pointer to an Ada function, pass the function as a callback to C and then C can call the function. The only restriction is applying a "pragma Convention" to the function indicating that is to be used by C, and handling any exception inside the function, so it doesn't reach C (although I've seen exceptions skipping the C functions on the call stack and reaching the outer Ada functions, butnot sure if you're playing with fire there).
The difficult part would be to make a thick binding wrapping the function, so that the user doesn't have to worry about passing special types for interfacing to C. In the few cases in SFML where a callback is passed, it just requests the user data as void * and I've left it with the equivalent type in Ada (System.Address). I guess it could be improved using a generic to wrap the low level details to the user, but I haven't tried.
By default, there's no garbage collection in Ada, so it wouldn't interfere in that case. Ideally, although I've not implemented it (yet?) for my SFML binding, every object should be wrapped, so memory is managed based on scopes, to alleviate the burden on the user. I'm not sure, though, if that would also lead to similar problems to those of the garbage collector.
I'll try having a gui class in C by the end of next week, then I will hopefully have a better idea if there might be other difficulties. I imagine processing events and handling callbacks will be the parts that need attention, for the rest the Ada binding probably doesn't need any internal logic.
While the code is not finished yet, CTGUI should now be in a state where it would be possible to build a binding on top of it. The example code in the examples folder should provide a good overview of what needs to be supported by a binding: if that code can be converted into a binding then all remaining stuff (more widgets and more functions) should be easy to add.
Here are some things to keep in mind if you would create a binding:
- Widget inheritance. All widgets of any type are stored as
tguiWidget*in C. Functions available for any widget (those provided in c++ in thetgui::Widgetclass) will start withtguiWidget_while functions for e.g. buttons (those provided in c++ in thetgui::Buttonclass) will start withtguiButton_. This was done to not duplicate all functions from the base class for every widget type, and to allow the other bindings to have inheritance again (in the C# binding I had a Button class that inherited from a Widget class). Depending on how you are creating the binding, this design might simplify or complicate things. - A similar inheritance strategy is now used for the tguiGui class: the
create,handleEventandfreefunctions are part of the backend-specific class, while all other functions (e.g.addanddraw) are available with thetguiGui_prefix. - Colors are plain structs that aren't allocated with create and free functions (like several other structs such as FloatRect), but unlike other such structs they are always passed around as pointers. This is because in TGUI a color can be set to be empty/unset. Getters for colors in CTGUI can in fact return nullptr values instead of a pointer to a valid color.
- CSFML 2.6.0 was missing a function that I needed. I've sent a PR for this that was merged, but you will need the latest CSFML version from the 2.6.x branch as CTGUI currently won't compile when using 2.6.0 (and CSFML 3.0 is currently also not supported).
- I've implemented 2 backends, one for SFML and one for SDL. The binding only needs to implement SFML, but you will need to tell CTGUI which backend to build by passing
-DCTGUI_BACKEND=CSFML_BACKENDto CMake when building CTGUI (and you can use-DTGUI_BACKEND=Customwhen building TGUI). - Many small features may still be missing. CTGUI only contains what existed in TGUI 0.8 + everything that I needed for the new API design. So certain functions in widgets are still be missing and will be added later.
- So far I've only tested compiling the code on linux with GCC. Compiling with MSVC or linking statically was not tested yet.
Thanks for the detailed explanations. I will take a look when time permits. I will have to see if a mainly thin binding, as for CSFML, is possible, or I will have to go for a thicker layer to provide inheritance and type safety.
I've got already a semi-working binding, using a thin layer, as for SFML. The example is working except for the callback, I don't know why, the same approach worked for SFML. Valgrind is not detecting something wrong on that part. I will need to continue analyzing it.
Silly mistake. The example is working now!
I've finished updating the C binding. The "1.x-WIP" branch has been removed and replaced by a new "1.x" branch.
There are still some improvements to be made, and I probably missed some stuff here and there, but CTGUI is now tracking the latest TGUI version again.