For the purposes of owlkettle I'm currently trying to wrap GtkBuilder. For the purposes of that, I need to figure out how to get GTK to instantiate a GTK_WIDGET and hand me a pointer to that via said GtkBuilder.
Now I have the necessary procs to instantiate a GtkBuilder, they can be easily bound. However, instantiating a GTK_WIDGET requires a piece of C syntax that I don't know how to replicate in nim.
Something like this (lifted from a tutorial page for gtkbuilder with C code):
build = gtk_builder_new_from_file ("tfe3.ui");
win = GTK_WIDGET (gtk_builder_get_object (build, "win"));
"win" is some id defined on said tfe3.ui file, think HTML tag id, basically that.
I can replicate this somewhat already, but the expression GTK_WIDGET I don't know how to replicate. It's not a proc I can bind and there are no bindable constructor procs, so what do I do to replicate instantiating a GTK_WIDGET?
This is basically what I already have bar the last line which obviously doesn't work
type GtkWidget = distinct pointer
type GtkBuilder = distinct pointer
type GObject = distinct pointer
proc buildWidgetFromUIString(uiString: string): GtkWidget
let builder = newBuilderFromString(uiString) # this is a nim proc that creates a builder, it's already wrapped and works.
let gObj: GObject = gtk_builder_get_object(builder, "someIdOnUiString")
let widget: GtkWidget = GTK_WIDGET (gObj) # This line obviously doesn't work, but what to replace it with that does work?
C Macros are a bit annoying to deal with, but it's possible. You end up needing to use some {.emit: "...".} code blocks along with some static strings.
I did some work with similar macros for Zephyr and put it in my macro library cdecl <https://github.com/elcritch/cdecl#nimcdeclmacro-crawstr-example>_.
Okay I worked my way somewhat through emit.
So I basically need these 2 expressions of sorts:
Now my main problem is just figuring out the precise syntax for both.
proc getWidget*(builder: Builder, id: string): GtkWidget =
let gObj: pointer = gtk_builder_get_object(builder.gtk, id.cstring)
{.emit: "#include <gtk/gtk.h>".}
{.emit: ["GTK_WIDGET (", gObj, ");"].}
return GtkWidget(gObj)
Isn't doing it, and neither is using ` {.emit: "#include <gtk/gtkwidget.h>".} instead =/I may just be generally struggling here because I never had to set up this before. Where do I see which header files are available?
After looking at other code examples I feel like {.emit: "#include <gtk/gtk.h>".} should work, but all I get is
CC: ../../owlkettle/builder.nim
/home/philipp/.cache/nim/scale_d/@m..@s..@[email protected]:54:10: fatal error: gtk/gtk.h: No such file or directory
54 | #include <gtk/gtk.h>
| ^~~~~~~~~~~
compilation terminated.
Which means the corresponding code is not available of course, given how much gtk stuff I've got installed I was just convinced it was already there.
Wait... that means I can do this entirely without any special emission of things whatsoever.
proc getWidget*(builder: Builder, id: string): GtkWidget =
let gObj: pointer = gtk_builder_get_object(builder.gtk, id.cstring)
return gObj.GtkWidget
Boom done
Nice! Yah if you can avoid needing to deal with emit and raw C token directly that's best.
I should amend my statement above for others in similar cases. Emitting raw C code is only needed for C Macros which require raw C tokens or raw string literals "some string". Those cases are where the C macros does concatenation or defines a new C variable. That's when you could use my cdeclmacro to handle the emit stuff for you (plus it's unit tested). Even then, it can be better to go through the C macro, figure out what it's doing and then manually implement it in Nim.
If the C macro doesn't do any funny concatenation or other token then you can import the macro as if it were a normal importc C function.