From 9a65ffabeb8781f5556973fa8b6fd93439f062eb Mon Sep 17 00:00:00 2001 From: Jakob Gillich Date: Fri, 9 Aug 2024 18:25:42 +0200 Subject: [PATCH] Generic bind funcs --- go.work | 6 ++++ pkg/core/glib/bind.go | 74 ++++++++++++++++++++++++++++++++++++++ pkg/core/glib/glib.go | 38 -------------------- pkg/core/glib/glib.go.h | 2 ++ pkg/core/test/glib_test.go | 16 +++++---- 5 files changed, 91 insertions(+), 45 deletions(-) create mode 100644 go.work create mode 100644 pkg/core/glib/bind.go diff --git a/go.work b/go.work new file mode 100644 index 000000000..6502fe888 --- /dev/null +++ b/go.work @@ -0,0 +1,6 @@ +go 1.22.0 + +use ( + . + ./pkg +) diff --git a/pkg/core/glib/bind.go b/pkg/core/glib/bind.go new file mode 100644 index 000000000..194ac2040 --- /dev/null +++ b/pkg/core/glib/bind.go @@ -0,0 +1,74 @@ +package glib + +import ( + "reflect" + "unsafe" + "sync" + + "github.com/diamondburned/gotk4/pkg/core/gbox" +) + +// #include +// #include +// #include "glib.go.h" +import "C" + +var bindingNames sync.Map // map[reflect.Type]C.GQuark + +// Associate value with object +func Bind[T any](obj Objector, value T) { + object := BaseObject(obj) + name := bindingName[T]() + + ptr := C.gpointer(gbox.Assign(value)) + + C.g_object_set_data_full(object.native(), (*C.gchar)(name), ptr, (*[0]byte)(C._gotk4_data_destroy)) +} + +// Disassociate value from object +func Unbind[T any](obj Objector) { + name := bindingName[T]() + + ptr := C.g_object_steal_data(BaseObject(obj).native(), (*C.gchar)(name)) + defer gbox.Delete(uintptr(ptr)) +} + +// Obtain value associated with object +func Bounded[T any](obj Objector) *T { + name := bindingName[T]() + + ptr := C.g_object_get_data(BaseObject(obj).native(), name) + + value, ok := gbox.Get(uintptr(ptr)).(T) + if !ok { + return nil + } + + return &value +} + +func bindingName[T any]() *C.gchar { + t := reflect.TypeFor[T]() + + if v, ok := bindingNames.Load(t); ok { + quark := v.(C.GQuark) + return C.g_quark_to_string(quark) + } + + name := "_gotk4_" + t.String() + + nameC := C.CString(name) + defer C.free(unsafe.Pointer(nameC)) + + quark := C.g_quark_from_string(nameC) + if v, lost := bindingNames.LoadOrStore(t, quark); lost { + quark = v.(C.GQuark) + } + + return C.g_quark_to_string(quark) +} + +//export _gotk4_data_destroy +func _gotk4_data_destroy(ptr C.gpointer) { + gbox.Delete(uintptr(ptr)) +} \ No newline at end of file diff --git a/pkg/core/glib/glib.go b/pkg/core/glib/glib.go index ce6d60c0f..2ffb7267e 100644 --- a/pkg/core/glib/glib.go +++ b/pkg/core/glib/glib.go @@ -589,9 +589,6 @@ type Objector interface { NotifyProperty(string, func()) SignalHandle ObjectProperty(string) interface{} SetObjectProperty(string, interface{}) - ObjectData(string) interface{} - SetObjectData(string, interface{}) - StealObjectData(name string) interface{} FreezeNotify() ThawNotify() StopEmission(string) @@ -871,41 +868,6 @@ func (v *Object) NotifyProperty(property string, f func()) SignalHandle { ) } -// Gets a named field from the objects table of associations -func (v *Object) ObjectData(name string) interface{} { - cstr := C.CString(name) - defer C.free(unsafe.Pointer(cstr)) - - ptr := C.g_object_get_data(v.native(), (*C.gchar)(cstr)) - runtime.KeepAlive(v) - - return gbox.Get(uintptr(ptr)) -} - -// Each object carries around a table of associations from strings to pointers. This function lets you set an association. -func (v *Object) SetObjectData(name string, value interface{}) { - cstr := C.CString(name) - defer C.free(unsafe.Pointer(cstr)) - - ptr := C.gpointer(gbox.Assign(value)) - - C.g_object_set_data(v.native(), (*C.gchar)(cstr), ptr) - runtime.KeepAlive(v) -} - -// Remove a specified datum from the object’s data associations -func (v *Object) StealObjectData(name string) interface{} { - cstr := C.CString(name) - defer C.free(unsafe.Pointer(cstr)) - - ptr := C.g_object_steal_data(v.native(), (*C.gchar)(cstr)) - defer gbox.Delete(uintptr(ptr)) - - runtime.KeepAlive(v) - - return gbox.Get(uintptr(ptr)) -} - // FreezeNotify increases the freeze count on object. If the freeze count is // non-zero, the emission of “notify” signals on object is stopped. The signals // are queued until the freeze count is decreased to zero. Duplicate diff --git a/pkg/core/glib/glib.go.h b/pkg/core/glib/glib.go.h index 62ce6ba11..ad0a2a2e2 100644 --- a/pkg/core/glib/glib.go.h +++ b/pkg/core/glib/glib.go.h @@ -109,4 +109,6 @@ static void init_i18n(const char *domain, const char *dir) { static const char *localize(const char *string) { return _(string); } +extern void _gotk4_data_destroy(gpointer ptr); + #endif diff --git a/pkg/core/test/glib_test.go b/pkg/core/test/glib_test.go index c52ee1210..69419a5a5 100644 --- a/pkg/core/test/glib_test.go +++ b/pkg/core/test/glib_test.go @@ -2,20 +2,22 @@ package test import ( "testing" + "runtime" - "github.com/diamondburned/gotk4/pkg/gtk/v4" + "github.com/diamondburned/gotk4/pkg/core/glib" + "github.com/diamondburned/gotk4/pkg/gio/v2" ) func TestObjectData(t *testing.T) { - gtk.Init() + app := gio.NewApplication("foo.bar", gio.ApplicationFlagsNone) - label := gtk.NewLabel("label") + glib.Bind(app, "foo") - label.SetObjectData("foo", "bar") - - if label.ObjectData("foo") != "bar" { + if value := glib.Bounded[string](app); value == nil || *value != "foo" { t.Fatal("returned data did not match expected data") } - label.StealObjectData("foo") + glib.Unbind[string](app) + + runtime.GC() }