First time posting here. I saw a youtube video last night and over the last few hours have grown somewhat fond of Crystal and I’m trying to build a shard for ui automation (automating mouse clicks, keyboard inputs, etc.). As far as I can tell, the only way to do this is by linking and calling C functions. Probably a noob question, but does Crystal support C code being included in shards and if so, how?
Edit: I’ve already figured out how to link C code, I’m just not sure how that works with the shard system.
Code in a shard is like any other Crystal code. You can just declare C bindings and then you can use them in Crystal code. Calling functions in C bindings works essentially the same as methods defined in Crystal. For example, you can define the bindings in one shard and use them in another.
I suppose the only challenge of a shard with bindings to an external library is that the shard is not self-contained. In order to use it, you need not just the shard itself but also the library. And that get’s kind of complicated because there’s no standard way to do that across different platforms.
The best you can do is to properly document the dependencies and how to get them, so that users of the shard know what they have to do.
Thank you for your response, that seems to make some sense. Is the way I’ve done it here bad? I apologize for the noob questions, I’m not exactly the best C programmer either, just cobbled this together from the X11/Xlib.h docs last night.
Well, I don’t really understand why you actually write C code. You can just implement those custom C functions in Crystal directly and don’t hassle with C.
You just have to define the bindings for libx11 instead of the bindings for your custom libraries.
I wasn’t aware that it was possible to do so. Again, I’m a noob here so that’s probably why, but I was under the impression that since there are multiple lines of c code at work for any given function, that it was necessary to link some c object files themselves. Are there any tutorials on how I might do this without such difficulties. When I was looking into it it seemed that there wasn’t a way to do it like that.
Oh yeah, you can absolutely do that! You can think of lib declarations as the .h file ported to Crystal. It’s fantastic! For example:
lib LibX11
alias Int = LibC::Int # Lets you use C's `int` type
struct Display
# define struct members with Crystal type signatures ...
foo : Int
bar : String # (`char*` gets automatically promoted to a String, but you can also use Char*)
end
# `Display* XOpenDisplay(int value)` aliased as `open_display`
fun open_display = XOpenDisplay(value : Int) : Display*
end
module X11
class Display
@display : Pointer(LibX11::Display) = LibX11.open_display(0)
def finalize
# Let GC free your pointers
LibX11.however_you_free_pointers_with_this_lib(@display)
end
end
end
So whenever you call X11::Display.new it will automatically call the XOpenDisplay(0) function in C for you and mop it up when the object gets garbage-collected with the finalize hook method so you don’t have to manage your own memory — finalize is basically your best friend when interacting with C libraries.
Ok this for the most part makes sense although I’m still unclear as to the purpose of the foo and bar members of the Display struct are. Are those supposed to be replaced with members of the Display struct in the X11 library?