Open wildeyedskies opened 1 year ago
However, if you change the C call to instead take the pointer of the proc, you get a error, so I'm not entirely sure what's going on here.
Here is the explanation why your attempt to fix didn't work.
Here is the explanation why your attempt to fix didn't work.
I don't think boxing is the reason for this. The signature for Gio.AsyncReadyCallback is actually not a Pointer(Void)
, so I'm pretty sure the libgen isn't working correctly.
I ran into this while trying to use Gio.Task which generates mismatched bindings between LibGio
and Gio
.
lib LIbGio
# generates the alias, but doesn't use it.
alias AsyncReadyCallback = Pointer(LibGObject::Object), Pointer(LibGio::AsyncResult), Pointer(Void) -> Void
fun g_task_new(source_object : Pointer(Void), cancellable : Pointer(Void), callback : Void*, callback_data : Pointer(Void)) : Pointer(Void)
end
module Gio
def self.new(source_object : GObject::Object?, cancellable : Gio::Cancellable?, callback : Gio::AsyncReadyCallback?, callback_data : Pointer(Void)?) : self
# g_task_new: (Constructor)
# @source_object: (nullable)
# @cancellable: (nullable)
# @callback: (nullable)
# @callback_data: (nullable)
# Returns: (transfer full)
# Generator::NullableArrayPlan
source_object = if source_object.nil?
Pointer(Void).null
else
source_object.to_unsafe
end
# Generator::NullableArrayPlan
cancellable = if cancellable.nil?
Pointer(Void).null
else
cancellable.to_unsafe
end
# Generator::NullableArrayPlan
callback_data = if callback_data.nil?
Pointer(Void).null
else
callback_data.to_unsafe
end
# C call
_retval = LibGio.g_task_new(source_object, cancellable, callback, callback_data)
# Return value handling
Gio::Task.new(_retval, GICrystal::Transfer::Full)
end
end
There's also a clear problem with trying to call #to_unsafe
on a callback_data, which is a Pointer(Void)
, but that's another issue.
Unfortunately, I'm not sure there are any workarounds that I can find.
Monkey patching isn't possible as you can't monkey patch C lib bindings. Trying to override the binding.yml
to use lib_ignore
for Gio also proves to be pretty difficult, as it can't conflict with Gio's already present binding.yml
Trying to use shard.override.yml
to use a separate gio.cr
with a differen't binding.yml
also ignores the binding.yml in gio.cr.
info - Pango - No binding config found for Gio-2.0.
Putting binding.yml
really anywhere in the project directory causes compile errors for the project like:
Error: can't find file '../../../../../src/bindings/g_lib/error.cr' relative to '/Users/skinnyjames/dsrc/big_editor/lib/gi-crystal/src/auto/g_lib-2.0/g_lib.cr'bi
So it seems that binding.yml
is coupled with the lib repo, and the lib repo can't be swapped out.
While the lib is cool, I can't seem to make an app do moderate computation without blocking the UI thread. Definitely open to suggestions. It'd be really great if Lib<Gio/Gtk/whatever>
generations were decoupled from their corresponding Gio/Gtk/whatever
abstractions, which make it a lot more complicated to work with, especially when there are bugs.
The Lib*
objects must be coupled only with other Lib*
form C libraries it depends, any other coupling must be interpreted as a bug. I mean, must be possible to monkey patch things to fix the gi-crystal bugs.
Callback need more <3 for sure. On top of my head it needs:
ClosureManager
to keep the Box objects in memory while the callback can be called.ClosureManager
to avoid memory leaks.It's possible to mimic a async API in libtest to be able to fix and test these issues in gi-crystal.
I'm trying to use Gio::Subprocess
and this issue is now blocking me too 😬.
The problem is that the method doesn't have a DestroyNotify
parameter, so GICrystal doesn't recognize it as a callback.
Anyway... I also need to think a better way to map these GObject assync API to Crystal... probably:
proc = Gio::Subprocess.new(argv: ["ls", "-l"], flags: :none)
proc.wait_async(cancelable) do |result|
...
end
This would cause GI-Crystal to call g_subprocess_wait_async
with GAsyncReadyCallback
being a wrapper function that would call the user callback then de-register itself from ClosureManager.
Here´s a monkey patch I did to be able to use GtkSource::FileSaver#save_async
from GTKSourceView.
module GtkSource
class FileSaver
def save_async(io_priority : GLib::Priority, cancellable : Gio::Cancellable? = nil, &callback : Gio::AsyncReadyCallback) : Nil
c_callback = Pointer(Void).null
if callback
c_callback = ->(gobj : Void*, result : Void*, box : Void*) {
unboxed_callback = Box(Gio::AsyncReadyCallback).unbox(box)
unboxed_callback.call(GObject::Object.new(gobj, :none), Gio::AbstractAsyncResult.new(result, :none))
GICrystal::ClosureDataManager.deregister(box)
nil
}.pointer
end
# C call
box = Box.box(callback)
GICrystal::ClosureDataManager.register(box)
LibGtkSource.gtk_source_file_saver_save_async(to_unsafe, io_priority, cancellable, Pointer(Void).null, Pointer(Void).null, Pointer(Void).null, c_callback, box)
end
end
end
I have plans to let the generator generate something similar to this, but passing to the block a generated type base don what the *_finish
methods returns, e.g. in this case it would be GtkSource::FileSaver::SaveAsyncResult
, with the method value
returning what the gtksource_file_saver_save_finish
would return, and more methods for any out parama of the *_finish
functions.
In case of error, any of these methods would just thrown the corresponding exception.
As usual... I'm not sure when I'll implement this, but at least it's all clear how to do it.
If you try to compile this branch of my project, you will get the following error.
https://gitlab.gnome.org/wildeyedskies/wince/-/tree/fix-geoclue-init
This results from the generated code function requiring a Gio::AsyncReadyCallback, but the C call expects a pointer for the callback.
However, if you change the C call to instead take the pointer of the proc, you get a error, so I'm not entirely sure what's going on here.