/* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */ /* * Copyright (C) 2002-2023 Ruby-GNOME Project Team * Copyright (C) 2002,2003 Masahiro Sakai * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ #include "rbgprivate.h" #define RG_TARGET_NAMESPACE cClosure static ID id_call; static gboolean rclosure_initialized = FALSE; #define TAG_SIZE 64 typedef struct _GRClosure GRClosure; struct _GRClosure { GClosure closure; VALUE callback; VALUE extra_args; VALUE rb_holder; GList *objects; GValToRValSignalFunc g2r_func; RGClosureCallFunc call_func; gchar tag[TAG_SIZE]; }; static VALUE rclosure_default_g2r_func(guint num, const GValue *values) { guint i; VALUE args = rb_ary_new_capa(num); for (i = 0; i < num; i++) rb_ary_store(args, i, GVAL2RVAL(&values[i])); return args; } struct marshal_arg { GClosure* closure; GValue* return_value; guint n_param_values; const GValue* param_values; gpointer invocation_hint; gpointer marshal_data; }; static int rclosure_alive_p(GRClosure *rclosure) { return !NIL_P(rclosure->rb_holder); } static VALUE rclosure_marshal_do(VALUE arg_) { VALUE ret = Qnil; struct marshal_arg *arg; GRClosure *rclosure; GValue *return_value; arg = (struct marshal_arg *)arg_; rclosure = (GRClosure *)(arg->closure); return_value = arg->return_value; if (rclosure_alive_p(rclosure)) { guint n_param_values; const GValue *param_values; /* gpointer invocation_hint;*/ /* gpointer marshal_data; */ GValToRValSignalFunc g2r_func; VALUE callback; VALUE extra_args; VALUE args; n_param_values = arg->n_param_values; param_values = arg->param_values; /* invocation_hint = arg->invocation_hint; */ /* marshal_data = arg->marshal_data; */ callback = rclosure->callback; extra_args = rclosure->extra_args; if (rclosure->call_func) { RGClosureCallData data; data.return_value = return_value; data.n_param_values = n_param_values; data.param_values = param_values; data.callback = callback; data.extra_args = extra_args; rclosure->call_func(&data); return Qnil; } if (rclosure->g2r_func) { g2r_func = (GValToRValSignalFunc)rclosure->g2r_func; } else { g2r_func = (GValToRValSignalFunc)rclosure_default_g2r_func; } args = (*g2r_func)(n_param_values, param_values); if (!NIL_P(extra_args)) { args = rb_ary_concat(args, extra_args); } ret = rb_apply(callback, id_call, args); } else { g_warning("GRClosure invoking callback: already destroyed: %s", rclosure->tag[0] ? rclosure->tag : "(anonymous)"); } if (return_value && G_VALUE_TYPE(return_value)) rbgobj_rvalue_to_gvalue(ret, return_value); return Qnil; } static void rclosure_marshal(GClosure* closure, GValue* return_value, guint n_param_values, const GValue* param_values, gpointer invocation_hint, gpointer marshal_data) { struct marshal_arg arg; if (!rclosure_initialized) { g_closure_invalidate(closure); return; } arg.closure = closure; arg.return_value = return_value; arg.n_param_values = n_param_values; arg.param_values = param_values; arg.invocation_hint = invocation_hint; arg.marshal_data = marshal_data; G_PROTECT_CALLBACK(rclosure_marshal_do, &arg); } static void g_rclosure_detach_raw_gobject(GClosure *closure, GObject *gobject); static void rclosure_weak_notify(gpointer data, GObject* where_the_object_was); static void rclosure_invalidate(G_GNUC_UNUSED gpointer data, GClosure *closure) { GRClosure *rclosure = (GRClosure *)closure; GList *next; for (next = rclosure->objects; next; next = next->next) { GObject *object = G_OBJECT(next->data); if (!NIL_P(rclosure->rb_holder)) { g_rclosure_detach_raw_gobject(closure, object); } g_object_weak_unref(object, rclosure_weak_notify, rclosure); g_closure_unref(closure); } g_list_free(rclosure->objects); rclosure->objects = NULL; if (!NIL_P(rclosure->rb_holder)) { RTYPEDDATA_DATA(rclosure->rb_holder) = NULL; rclosure->rb_holder = Qnil; } } static void gr_closure_holder_mark(void *data) { GRClosure *rclosure = data; if (!rclosure) return; rb_gc_mark(rclosure->callback); rb_gc_mark(rclosure->extra_args); } static void gr_closure_holder_free(void *data) { GRClosure *rclosure = data; if (!rclosure) return; GClosure *closure = (GClosure *)rclosure; gboolean last_reference = (closure->ref_count == 1); g_closure_unref(closure); if (!last_reference) { g_closure_invalidate(closure); } } static const rb_data_type_t rbg_closure_holder_type = { "GLib::ClosureHolder", { gr_closure_holder_mark, gr_closure_holder_free, }, NULL, NULL, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE, }; static GClosure * g_rclosure_new_raw(VALUE callback_proc, VALUE extra_args, GValToRValSignalFunc g2r_func, RGClosureCallFunc call_func) { GClosure *closure = g_closure_new_simple(sizeof(GRClosure), NULL); GRClosure *rclosure = (GRClosure *)closure; rclosure->g2r_func = g2r_func; rclosure->call_func = call_func; rclosure->objects = NULL; rclosure->callback = callback_proc; rclosure->extra_args = extra_args; rclosure->rb_holder = TypedData_Wrap_Struct(rb_cObject, &rbg_closure_holder_type, rclosure); rclosure->tag[0] = '\0'; g_closure_ref(closure); g_closure_sink(closure); g_closure_set_marshal(closure, &rclosure_marshal); g_closure_add_invalidate_notifier(closure, NULL, rclosure_invalidate); return closure; } GClosure * g_rclosure_new(VALUE callback_proc, VALUE extra_args, GValToRValSignalFunc g2r_func) { return g_rclosure_new_raw(callback_proc, extra_args, g2r_func, NULL); } GClosure * g_rclosure_new_call(VALUE callback_proc, VALUE extra_args, RGClosureCallFunc call_func) { return g_rclosure_new_raw(callback_proc, extra_args, NULL, call_func); } static void rclosure_weak_notify(gpointer data, GObject *where_the_object_was) { GRClosure *rclosure = data; rclosure->objects = g_list_remove(rclosure->objects, where_the_object_was); g_closure_unref((GClosure *)rclosure); } void g_rclosure_attach(GClosure *closure, VALUE object) { GRClosure *rclosure = (GRClosure *)closure; rbgobj_add_relative(object, rclosure->rb_holder); } void g_rclosure_attach_gobject(GClosure *closure, VALUE object) { GRClosure *rclosure = (GRClosure *)closure; g_rclosure_attach(closure, object); GObject *gobject = RVAL2GOBJ(object); g_closure_ref(closure); g_object_weak_ref(gobject, rclosure_weak_notify, rclosure); rclosure->objects = g_list_prepend(rclosure->objects, gobject); } static void g_rclosure_detach_raw_gobject(GClosure *closure, GObject *gobject) { GRClosure *rclosure = (GRClosure *)closure; rbgobj_gobject_remove_relative(gobject, rclosure->rb_holder); } void g_rclosure_detach(GClosure *closure, VALUE object) { GRClosure *rclosure = (GRClosure *)closure; rbgobj_remove_relative(object, 0, rclosure->rb_holder); } void g_rclosure_detach_gobject(GClosure *closure, VALUE object) { GRClosure *rclosure = (GRClosure *)closure; g_rclosure_detach(closure, object); GObject *gobject = RVAL2GOBJ(object); g_object_weak_unref(gobject, rclosure_weak_notify, rclosure); rclosure_weak_notify(rclosure, gobject); } void g_rclosure_set_tag(GClosure *closure, const gchar *tag) { GRClosure *rclosure = (GRClosure *)closure; if (tag) { strncpy(rclosure->tag, tag, TAG_SIZE); rclosure->tag[TAG_SIZE - 1] = '\0'; } else { rclosure->tag[0] = '\0'; } } static void rclosure_end_proc(G_GNUC_UNUSED VALUE _) { rclosure_initialized = FALSE; } static void init_rclosure(void) { id_call = rb_intern("call"); rclosure_initialized = TRUE; rb_set_end_proc(rclosure_end_proc, Qnil); } /**********************************************************************/ static VALUE rg_initialize(VALUE self) { GClosure *closure = g_rclosure_new(rb_block_proc(), Qnil, NULL); G_INITIALIZE(self, closure); return self; } static VALUE rg_in_marshal_p(VALUE self) { GClosure* closure = RVAL2BOXED(self, G_TYPE_CLOSURE); return CBOOL2RVAL(closure->in_marshal); } static VALUE rg_invalid_p(VALUE self) { GClosure* closure = RVAL2BOXED(self, G_TYPE_CLOSURE); return CBOOL2RVAL(closure->is_invalid); } static VALUE rg_invalidate(VALUE self) { GClosure* closure = RVAL2BOXED(self, G_TYPE_CLOSURE); g_closure_invalidate(closure); return self; } /**********************************************************************/ void Init_gobject_gclosure(void) { VALUE RG_TARGET_NAMESPACE; init_rclosure(); RG_TARGET_NAMESPACE = G_DEF_CLASS(G_TYPE_CLOSURE, "Closure", rbg_mGLib()); RG_DEF_METHOD(initialize, 0); RG_DEF_METHOD_P(in_marshal, 0); RG_DEF_METHOD_P(invalid, 0); RG_DEF_METHOD(invalidate, 0); }