diff --git a/gir/girgen/file.go b/gir/girgen/file.go index 137131cea..0ee0f0713 100644 --- a/gir/girgen/file.go +++ b/gir/girgen/file.go @@ -216,6 +216,12 @@ func (f *GoFileGenerator) Generate() ([]byte, error) { fpen.Words("// #cgo CFLAGS: -Wno-deprecated-declarations") } + if defines := f.CDefines(); len(defines) > 0 { + for _, macro := range defines { + fpen.Linef("// #define %s", macro) + } + } + fpen.Words("// #include ") if incls := f.CIncludes(); len(incls) > 0 { for _, incl := range incls { @@ -317,6 +323,12 @@ func (f *GoFileGenerator) CIncludes() []string { return namespaceCIncludes(f.current, &f.header) } +// CIncludes returns this file's sorted C includes, including the repository's C +// includes. +func (f *GoFileGenerator) CDefines() []string { + return f.header.SortedCDefines() +} + func namespaceCIncludes(n *gir.NamespaceFindResult, h *file.Header) []string { extraIncludes := h.SortedCIncludes() diff --git a/gir/girgen/file/file.go b/gir/girgen/file/file.go index dfc51a262..d9c66377e 100644 --- a/gir/girgen/file/file.go +++ b/gir/girgen/file/file.go @@ -54,6 +54,7 @@ type Header struct { Marshalers []Marshaler Imports map[string]string CIncludes map[string]struct{} + Defines map[string]struct{} Packages map[string]struct{} // for pkg-config Callbacks map[string]struct{} // used for C blocks in general CallbackDelete bool @@ -273,6 +274,30 @@ func (h *Header) SortedCIncludes() []string { return includes } +// DefineC defines a macro using #define +func (h *Header) DefineC(macro string) { + if h.stop { + return + } + + if h.Defines == nil { + h.Defines = map[string]struct{}{} + } + + h.Defines[macro] = struct{}{} +} + +// SortedCDefines returns the list of C defines sorted. +func (h *Header) SortedCDefines() []string { + defines := make([]string, 0, len(h.Defines)) + for macro := range h.Defines { + defines = append(defines, macro) + } + + sort.Strings(defines) + return defines +} + // NeedsGLibObject adds the glib-object.h include and the glib-2.0 package. func (h *Header) NeedsGLibObject() { // Need this for g_value_get_boxed. diff --git a/gir/girgen/generators/iface/iface.go b/gir/girgen/generators/iface/iface.go index cde636bef..7e12940c3 100644 --- a/gir/girgen/generators/iface/iface.go +++ b/gir/girgen/generators/iface/iface.go @@ -338,6 +338,12 @@ func (g *Generator) Use(typ interface{}) bool { } for i, sig := range signals { + // signals with the G_SIGNAL_ACTION flag are there to be freely emitted by user code. They also can + // be thought of as methods on generic objects. + if sig.Action { + continue // TODO: generate a type safe call for emit instead, similar to how we do for connect + } + // A signal has 2 implied parameters: the instance (0th) parameter and // the final user_data parameter. param := &gir.Parameters{ diff --git a/gir/girgen/types/filter.go b/gir/girgen/types/filter.go index 9fc8a9948..5ab81a59e 100644 --- a/gir/girgen/types/filter.go +++ b/gir/girgen/types/filter.go @@ -143,27 +143,42 @@ func PreserveGetName(girType string) Preprocessor { }) } -// RenameEnumMembers renames all members of the matched enums. It is primarily +// RenameEnumMembers renames the c-identifier of members of the matched enum or bitfield. It is primarily // used to avoid collisions. func RenameEnumMembers(enum, regex, replace string) Preprocessor { girTypeMustBeVersioned(enum) re := regexp.MustCompile(regex) + return MapMembers(enum, func(member gir.Member) gir.Member { + parts := strings.SplitN(member.CIdentifier, "_", 2) + parts[1] = re.ReplaceAllString(parts[1], replace) + member.CIdentifier = parts[0] + "_" + parts[1] + + return member + }) +} + +// MapMembers allows you to freely change any property of the members of the given enum or bitfield type. +// it can be used to fix faulty girs, but also to change the behavior of the generator. +func MapMembers(enumOrBitfieldType string, fn func(member gir.Member) gir.Member) Preprocessor { + girTypeMustBeVersioned(enumOrBitfieldType) return PreprocessorFunc(func(repos gir.Repositories) { - result := repos.FindFullType(enum) + result := repos.FindFullType(enumOrBitfieldType) if result == nil { - log.Printf("GIR enum %q not found", enum) + log.Panicf("GIR enum or bitfield %q not found", enumOrBitfieldType) return } - enum, ok := result.Type.(*gir.Enum) - if !ok { - log.Panicf("GIR type %T is not enum", result.Type) - } - - for i, member := range enum.Members { - parts := strings.SplitN(member.CIdentifier, "_", 2) - parts[1] = re.ReplaceAllString(parts[1], replace) - enum.Members[i].CIdentifier = parts[0] + "_" + parts[1] + switch v := result.Type.(type) { + case *gir.Enum: + for i, member := range v.Members { + v.Members[i] = fn(member) + } + case *gir.Bitfield: + for i, member := range v.Members { + v.Members[i] = fn(member) + } + default: + log.Panicf("GIR type %T is not enum or bitfield", result.Type) } }) } diff --git a/gir/girgen/types/typeconv/c-go.go b/gir/girgen/types/typeconv/c-go.go index 8d2de8147..f24340005 100644 --- a/gir/girgen/types/typeconv/c-go.go +++ b/gir/girgen/types/typeconv/c-go.go @@ -445,6 +445,53 @@ func (conv *Converter) cgoConverter(value *ValueConverted) bool { } return true + case "Gst.MiniObject", "Gst.Structure", "Gst.Caps", "Gst.Buffer", "Gst.BufferList", "Gst.Memory", "Gst.Message", "Gst.Query", "Gst.Sample": + value.header.Import("runtime") + value.header.Import("unsafe") + value.header.ImportCore("gextras") + + // Require 1 pointer to avoid weird copies. + value.vtmpl(` + <.Out.Set> = <.OutCast 1>(gextras.NewStructNative(unsafe.Pointer(<.InNamePtr 1>))) + `) + if value.fail { + value.Logln(logger.Debug, "record set fail") + return false + } + + if value.TransferOwnership.TransferOwnership == "none" { + value.vtmpl("C.gst_mini_object_ref((*C.GstMiniObject)(unsafe.Pointer(<.InNamePtr 1>)))") + } + + if value.TransferOwnership.TransferOwnership != "borrow" { + value.vtmpl(` + runtime.SetFinalizer( + gextras.StructIntern(unsafe.Pointer(<.OutInPtr 1><.OutName>)), + func(intern *struct{ C unsafe.Pointer }) { + C.gst_mini_object_unref((*C.GstMiniObject)(intern.C)) + }) + `) + } + + if value.TransferOwnership.TransferOwnership == "borrow" && len(conv.Results) == 0 { + panic("result should borrow even though callable has no param") + } + + if value.TransferOwnership.TransferOwnership == "borrow" { + // the returned value must keep the instance alive, which is the first passed param, aka the receiver + value.vtmpl(fmt.Sprintf(` + runtime.SetFinalizer( // value is borrowed, don't clean up the receiver until dropped + gextras.StructIntern(unsafe.Pointer(<.OutInPtr 1><.OutName>)), + func(_ *struct{ C unsafe.Pointer }) { + runtime.KeepAlive(%s) + }, + )`, + conv.Results[0].InName, + )) + } + + return true + case "GObject.Object", "GObject.InitiallyUnowned": return value.cgoSetObject(conv) diff --git a/gir/girgen/types/typeconv/go-c.go b/gir/girgen/types/typeconv/go-c.go index e717c5bc3..0581bf26f 100644 --- a/gir/girgen/types/typeconv/go-c.go +++ b/gir/girgen/types/typeconv/go-c.go @@ -479,7 +479,7 @@ func (conv *Converter) gocConverter(value *ValueConverted) bool { value.header.NeedsExternGLib() value.p.Linef( - "%s = (*C.GClosure)(coreglib.NewClosure(coreglib.InternObject(%s), %s))", + "%s = (*C.GClosure)(coreglib.NewClosure(coreglib.BaseObject(%s), %s))", value.Out.Set, instance.In.Name, value.In.Name, ) @@ -605,7 +605,7 @@ func (conv *Converter) gocConverter(value *ValueConverted) bool { value.header.Import("unsafe") value.header.NeedsExternGLib() value.p.Linef( - "%s = %s(unsafe.Pointer(coreglib.InternObject(%s).Native()))", + "%s = %s(unsafe.Pointer(coreglib.BaseObject(%s).Native()))", value.Out.Set, value.OutCast(1), value.InNamePtrPubl(1), ) @@ -613,7 +613,7 @@ func (conv *Converter) gocConverter(value *ValueConverted) bool { // Caller is taking ownership, which means it will steal our // reference. Ensure that we take our own. value.p.Linef( - "C.g_object_ref(C.gpointer(coreglib.InternObject(%s).Native()))", + "C.g_object_ref(C.gpointer(coreglib.BaseObject(%s).Native()))", value.InNamePtrPubl(1), ) }