detached fe4d51f

GIR: Implement support for generating getters and setters for callbacks based on GIR instead of generating untyped pointers

Signed-off-by: Felicitas Pojtinger <felicitas@pojtinger.com>
commit
fe4d51fc03a1930e51935ba7013817e6f424de88
author
Felicitas Pojtinger · · 2025-10-04 06:34 UTC
committer
Jeroen Wijenbergh · · 2025-10-16 13:39 UTC
parent
c8c33c3
signature
unsigned
6 files · +213 -24
M gen.go +8 -1
@@ -31,7 +31,14 @@ func main() {
3131 p.First()
3232
3333 // Create the template
34- gotemp, err := template.New("go").Funcs(template.FuncMap{"conv": util.ConvertArgs, "convc": util.ConvertArgsComma}).ParseFiles("templates/go")
34+ gotemp, err := template.New("go").Funcs(template.FuncMap{
35+ "conv": util.ConvertArgs,
36+ "convc": util.ConvertArgsComma,
37+ "convcb": util.ConvertCallbackArgs,
38+ "convcd": util.ConvertArgsCommaDeref,
39+ "convd": util.ConvertArgsDeref,
40+ "convcbne": util.ConvertCallbackArgsNoErr,
41+ }).ParseFiles("templates/go")
3542 if err != nil {
3643 panic(err)
3744 }
@@ -31,7 +31,14 @@ func main() {
31 p.First()31 p.First()
32 32
33 // Create the template33 // Create the template
34- gotemp, err := template.New("go").Funcs(template.FuncMap{"conv": util.ConvertArgs, "convc": util.ConvertArgsComma}).ParseFiles("templates/go")34+ gotemp, err := template.New("go").Funcs(template.FuncMap{
35+ "conv": util.ConvertArgs,
36+ "convc": util.ConvertArgsComma,
37+ "convcb": util.ConvertCallbackArgs,
38+ "convcd": util.ConvertArgsCommaDeref,
39+ "convd": util.ConvertArgsDeref,
40+ "convcbne": util.ConvertCallbackArgsNoErr,
41+ }).ParseFiles("templates/go")
35 if err != nil {42 if err != nil {
36 panic(err)43 panic(err)
37 }44 }
M internal/gir/pass/pass.go +47 -10
@@ -114,6 +114,11 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
114114 constants[fn] = append(constants[fn], con.Template(ns.Name, p.Types))
115115 }
116116
117+ callbackDocs := make(map[string]string)
118+ for _, cb := range ns.Callbacks {
119+ callbackDocs[cb.Name] = cb.Doc.StringSafe()
120+ }
121+
117122 records := make(map[string][]types.RecordTemplate)
118123 recordLookup := make(map[string]bool)
119124 for _, rec := range ns.Records {
@@ -121,6 +126,7 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
121126 constructors := make([]types.FuncTemplate, len(rec.Constructors))
122127 receivers := make([]types.FuncTemplate, 0, len(rec.Methods))
123128 fields := make([]types.RecordField, 0, len(rec.Fields))
129+ callbackAccessors := make([]types.CallbackAccessor, 0)
124130 fn := rec.FilenameSafe()
125131 files = append(files, fn)
126132 for i, c := range rec.Constructors {
@@ -134,11 +140,40 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
134140 }
135141 for _, f := range rec.Fields {
136142 var _type string
137-
143+ var fieldName string
144+
138145 // Check if this field is a callback
139146 if f.Callback != nil {
140- // Callbacks in structs are function pointers, represented as uintptr
141147 _type = "uintptr"
148+ fieldName = "x" + util.SnakeToCamel(f.Name) // Prefix callback pointer fields with `x` to make them private
149+
150+ callbackName := util.SnakeToCamel(f.Name)
151+ args := f.Callback.Parameters.Template(ns.Name, "", p.Types, f.Callback.Throws)
152+ ret := f.Callback.ReturnValue.Template(ns.Name, "", p.Types, f.Callback.Throws)
153+
154+ apiTypes := args.API.Types
155+
156+ var doc string
157+ if f.Callback.Doc != nil && f.Callback.Doc.String != "" {
158+ doc = f.Callback.Doc.StringSafe()
159+ } else {
160+ baseClassName := strings.TrimSuffix(rec.Name, "Class")
161+ callbackName := baseClassName + util.SnakeToCamel(f.Name) + "Func"
162+
163+ if callbackDoc, exists := callbackDocs[callbackName]; exists && callbackDoc != "" {
164+ doc = callbackDoc
165+ } else {
166+ doc = f.Doc.StringSafe()
167+ }
168+ }
169+
170+ callbackAccessors = append(callbackAccessors, types.CallbackAccessor{
171+ Name: callbackName,
172+ Doc: doc,
173+ CallbackType: "func(" + strings.Join(apiTypes, ", ") + ") " + ret.Value,
174+ Args: args,
175+ Ret: ret,
176+ })
142177 } else {
143178 _type = f.Translate(ns.Name, p.Types)
144179 if _type == "" {
@@ -155,10 +190,11 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
155190 if _type == "string" {
156191 _type = "uintptr"
157192 }
193+ fieldName = util.SnakeToCamel(f.Name)
158194 }
159-
195+
160196 fields = append(fields, types.RecordField{
161- Name: util.SnakeToCamel(f.Name),
197+ Name: fieldName,
162198 Type: _type,
163199 })
164200 }
@@ -182,12 +218,13 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
182218 })
183219 }
184220 records[fn] = append(records[fn], types.RecordTemplate{
185- Name: name,
186- Doc: rec.Doc.StringSafe(),
187- Constructors: constructors,
188- Receivers: receivers,
189- Fields: fields,
190- TypeGetter: rec.GLibGetType,
221+ Name: name,
222+ Doc: rec.Doc.StringSafe(),
223+ Constructors: constructors,
224+ Receivers: receivers,
225+ Fields: fields,
226+ CallbackAccessors: callbackAccessors,
227+ TypeGetter: rec.GLibGetType,
191228 })
192229 recordLookup[name] = true
193230 }
@@ -114,6 +114,11 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
114 constants[fn] = append(constants[fn], con.Template(ns.Name, p.Types))114 constants[fn] = append(constants[fn], con.Template(ns.Name, p.Types))
115 }115 }
116 116
117+ callbackDocs := make(map[string]string)
118+ for _, cb := range ns.Callbacks {
119+ callbackDocs[cb.Name] = cb.Doc.StringSafe()
120+ }
121+
117 records := make(map[string][]types.RecordTemplate)122 records := make(map[string][]types.RecordTemplate)
118 recordLookup := make(map[string]bool)123 recordLookup := make(map[string]bool)
119 for _, rec := range ns.Records {124 for _, rec := range ns.Records {
@@ -121,6 +126,7 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
121 constructors := make([]types.FuncTemplate, len(rec.Constructors))126 constructors := make([]types.FuncTemplate, len(rec.Constructors))
122 receivers := make([]types.FuncTemplate, 0, len(rec.Methods))127 receivers := make([]types.FuncTemplate, 0, len(rec.Methods))
123 fields := make([]types.RecordField, 0, len(rec.Fields))128 fields := make([]types.RecordField, 0, len(rec.Fields))
129+ callbackAccessors := make([]types.CallbackAccessor, 0)
124 fn := rec.FilenameSafe()130 fn := rec.FilenameSafe()
125 files = append(files, fn)131 files = append(files, fn)
126 for i, c := range rec.Constructors {132 for i, c := range rec.Constructors {
@@ -134,11 +140,40 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
134 }140 }
135 for _, f := range rec.Fields {141 for _, f := range rec.Fields {
136 var _type string142 var _type string
137- 143+ var fieldName string
144+
138 // Check if this field is a callback145 // Check if this field is a callback
139 if f.Callback != nil {146 if f.Callback != nil {
140- // Callbacks in structs are function pointers, represented as uintptr
141 _type = "uintptr"147 _type = "uintptr"
148+ fieldName = "x" + util.SnakeToCamel(f.Name) // Prefix callback pointer fields with `x` to make them private
149+
150+ callbackName := util.SnakeToCamel(f.Name)
151+ args := f.Callback.Parameters.Template(ns.Name, "", p.Types, f.Callback.Throws)
152+ ret := f.Callback.ReturnValue.Template(ns.Name, "", p.Types, f.Callback.Throws)
153+
154+ apiTypes := args.API.Types
155+
156+ var doc string
157+ if f.Callback.Doc != nil && f.Callback.Doc.String != "" {
158+ doc = f.Callback.Doc.StringSafe()
159+ } else {
160+ baseClassName := strings.TrimSuffix(rec.Name, "Class")
161+ callbackName := baseClassName + util.SnakeToCamel(f.Name) + "Func"
162+
163+ if callbackDoc, exists := callbackDocs[callbackName]; exists && callbackDoc != "" {
164+ doc = callbackDoc
165+ } else {
166+ doc = f.Doc.StringSafe()
167+ }
168+ }
169+
170+ callbackAccessors = append(callbackAccessors, types.CallbackAccessor{
171+ Name: callbackName,
172+ Doc: doc,
173+ CallbackType: "func(" + strings.Join(apiTypes, ", ") + ") " + ret.Value,
174+ Args: args,
175+ Ret: ret,
176+ })
142 } else {177 } else {
143 _type = f.Translate(ns.Name, p.Types)178 _type = f.Translate(ns.Name, p.Types)
144 if _type == "" {179 if _type == "" {
@@ -155,10 +190,11 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
155 if _type == "string" {190 if _type == "string" {
156 _type = "uintptr"191 _type = "uintptr"
157 }192 }
193+ fieldName = util.SnakeToCamel(f.Name)
158 }194 }
159- 195+
160 fields = append(fields, types.RecordField{196 fields = append(fields, types.RecordField{
161- Name: util.SnakeToCamel(f.Name),197+ Name: fieldName,
162 Type: _type,198 Type: _type,
163 })199 })
164 }200 }
@@ -182,12 +218,13 @@ func (p *Pass) writeGo(r types.Repository, gotemp *template.Template, dir string
182 })218 })
183 }219 }
184 records[fn] = append(records[fn], types.RecordTemplate{220 records[fn] = append(records[fn], types.RecordTemplate{
185- Name: name,221+ Name: name,
186- Doc: rec.Doc.StringSafe(),222+ Doc: rec.Doc.StringSafe(),
187- Constructors: constructors,223+ Constructors: constructors,
188- Receivers: receivers,224+ Receivers: receivers,
189- Fields: fields,225+ Fields: fields,
190- TypeGetter: rec.GLibGetType,226+ CallbackAccessors: callbackAccessors,
227+ TypeGetter: rec.GLibGetType,
191 })228 })
192 recordLookup[name] = true229 recordLookup[name] = true
193 }230 }
M internal/gir/types/template.go +30 -2
@@ -89,14 +89,22 @@ func (f *funcArgsTemplate) AddPure(t string, n string, k Kind) {
8989 t = "uintptr"
9090 }
9191 case CallbackType:
92+ c = fmt.Sprintf("(*%s)(unsafe.Pointer(%s))", strings.TrimPrefix(t, "*"), n)
9293 t = "uintptr"
9394 case ClassesType:
9495 if stars == 0 {
9596 c = n
9697 t = "uintptr"
9798 } else {
98- c = fmt.Sprintf("%sNewFromInternalPtr(%s)", strings.TrimPrefix(t, "*"), n)
99- t = strings.Repeat("*", stars-1) + "uintptr"
99+ // Remove all dereference operators to get the base class name
100+ baseName := strings.TrimPrefix(t, strings.Repeat("*", stars))
101+ if stars > 1 {
102+ // For double pointers like **ParamSpec, we need to pass the double pointer directly
103+ c = fmt.Sprintf("(**%s)(unsafe.Pointer(%s))", baseName, n)
104+ } else {
105+ c = fmt.Sprintf("%sNewFromInternalPtr(%s)", baseName, n)
106+ }
107+ t = "uintptr"
100108 }
101109 case InterfacesType:
102110 if stars == 0 {
@@ -179,6 +187,23 @@ type RecordField struct {
179187 Type string
180188 }
181189
190+type CallbackAccessor struct {
191+ // Name is the Go name of the callback field (without x prefix)
192+ Name string
193+
194+ // Doc is the documentation for the callback
195+ Doc string
196+
197+ // CallbackType is the name of the callback function type
198+ CallbackType string
199+
200+ // Args are the callback function arguments template
201+ Args funcArgsTemplate
202+
203+ // Ret is the callback function return template
204+ Ret funcRetTemplate
205+}
206+
182207 type RecordTemplate struct {
183208 // Name is the name of the record given to the Go type declaration
184209 Name string
@@ -195,6 +220,9 @@ type RecordTemplate struct {
195220 // Fields is the list of record fields
196221 Fields []RecordField
197222
223+ // CallbackAccessors are the setter/getter methods for callback fields
224+ CallbackAccessors []CallbackAccessor
225+
198226 // TypeGetter is the function to get the GLib type
199227 TypeGetter string
200228 }
@@ -89,14 +89,22 @@ func (f *funcArgsTemplate) AddPure(t string, n string, k Kind) {
89 t = "uintptr"89 t = "uintptr"
90 }90 }
91 case CallbackType:91 case CallbackType:
92+ c = fmt.Sprintf("(*%s)(unsafe.Pointer(%s))", strings.TrimPrefix(t, "*"), n)
92 t = "uintptr"93 t = "uintptr"
93 case ClassesType:94 case ClassesType:
94 if stars == 0 {95 if stars == 0 {
95 c = n96 c = n
96 t = "uintptr"97 t = "uintptr"
97 } else {98 } else {
98- c = fmt.Sprintf("%sNewFromInternalPtr(%s)", strings.TrimPrefix(t, "*"), n)99+ // Remove all dereference operators to get the base class name
99- t = strings.Repeat("*", stars-1) + "uintptr"100+ baseName := strings.TrimPrefix(t, strings.Repeat("*", stars))
101+ if stars > 1 {
102+ // For double pointers like **ParamSpec, we need to pass the double pointer directly
103+ c = fmt.Sprintf("(**%s)(unsafe.Pointer(%s))", baseName, n)
104+ } else {
105+ c = fmt.Sprintf("%sNewFromInternalPtr(%s)", baseName, n)
106+ }
107+ t = "uintptr"
100 }108 }
101 case InterfacesType:109 case InterfacesType:
102 if stars == 0 {110 if stars == 0 {
@@ -179,6 +187,23 @@ type RecordField struct {
179 Type string187 Type string
180 }188 }
181 189
190+type CallbackAccessor struct {
191+ // Name is the Go name of the callback field (without x prefix)
192+ Name string
193+
194+ // Doc is the documentation for the callback
195+ Doc string
196+
197+ // CallbackType is the name of the callback function type
198+ CallbackType string
199+
200+ // Args are the callback function arguments template
201+ Args funcArgsTemplate
202+
203+ // Ret is the callback function return template
204+ Ret funcRetTemplate
205+}
206+
182 type RecordTemplate struct {207 type RecordTemplate struct {
183 // Name is the name of the record given to the Go type declaration208 // Name is the name of the record given to the Go type declaration
184 Name string209 Name string
@@ -195,6 +220,9 @@ type RecordTemplate struct {
195 // Fields is the list of record fields220 // Fields is the list of record fields
196 Fields []RecordField221 Fields []RecordField
197 222
223+ // CallbackAccessors are the setter/getter methods for callback fields
224+ CallbackAccessors []CallbackAccessor
225+
198 // TypeGetter is the function to get the GLib type226 // TypeGetter is the function to get the GLib type
199 TypeGetter string227 TypeGetter string
200 }228 }
M internal/gir/util/util.go +76 -0
@@ -9,6 +9,18 @@ import (
99 "strings"
1010 )
1111
12+var (
13+ // Variable names that should not be dereferenced when using ConvertPtr() in handlePtr mode
14+ // TODO: This was mostly discovered via trial and error, and might point towards issues in
15+ // the GIR files
16+ specialConvertPtrVars = []string{
17+ "ModelVar",
18+ "TreeModelVar",
19+ "OutChildVar",
20+ "ChildVar",
21+ }
22+)
23+
1224 // delimToCamel to camel converts a string with parts separated by `delim` to CamelCase
1325 func delimToCamel(s string, delim string) string {
1426 var sb strings.Builder
@@ -123,6 +135,70 @@ func ConvertArgsComma(a []string) string {
123135 return ", " + strings.Join(a, ", ")
124136 }
125137
138+func convertCallbackArgs(a []string, prependComma, skipEmpty, skipErr, handlePtr bool) string {
139+ var validArgs []string
140+ for _, arg := range a {
141+ if skipEmpty && arg == "" {
142+ continue
143+ }
144+ if skipErr && arg == "&cerr" {
145+ continue
146+ }
147+
148+ if strings.Contains(arg, "{Ptr:") {
149+ if !handlePtr {
150+ // For ConvertCallbackArgs: remove * prefix and add &
151+ arg = strings.TrimPrefix(arg, "*")
152+ }
153+ validArgs = append(validArgs, "&"+arg)
154+ } else if strings.Contains(arg, "ConvertPtr(") && handlePtr {
155+ isSpecialVar := false
156+ for _, specialVar := range specialConvertPtrVars {
157+ if strings.Contains(arg, specialVar) {
158+ isSpecialVar = true
159+
160+ break
161+ }
162+ }
163+
164+ if isSpecialVar {
165+ validArgs = append(validArgs, arg)
166+ } else {
167+ validArgs = append(validArgs, "*"+arg)
168+ }
169+ } else {
170+ validArgs = append(validArgs, arg)
171+ }
172+ }
173+
174+ if len(validArgs) == 0 {
175+ return ""
176+ }
177+
178+ result := strings.Join(validArgs, ", ")
179+ if prependComma {
180+ return ", " + result
181+ }
182+
183+ return result
184+}
185+
186+func ConvertCallbackArgs(a []string) string {
187+ return convertCallbackArgs(a, false, false, false, false)
188+}
189+
190+func ConvertArgsCommaDeref(a []string) string {
191+ return convertCallbackArgs(a, true, true, false, true)
192+}
193+
194+func ConvertArgsDeref(a []string) string {
195+ return convertCallbackArgs(a, false, true, false, true)
196+}
197+
198+func ConvertCallbackArgsNoErr(a []string) string {
199+ return convertCallbackArgs(a, false, true, true, true)
200+}
201+
126202 // ConstructorName returns a Go friendly constructor name given the raw constructor name `name` and the class/record name `outer`
127203 func ConstructorName(name string, outer string) string {
128204 cname := SnakeToCamel(name)
@@ -9,6 +9,18 @@ import (
9 "strings"9 "strings"
10 )10 )
11 11
12+var (
13+ // Variable names that should not be dereferenced when using ConvertPtr() in handlePtr mode
14+ // TODO: This was mostly discovered via trial and error, and might point towards issues in
15+ // the GIR files
16+ specialConvertPtrVars = []string{
17+ "ModelVar",
18+ "TreeModelVar",
19+ "OutChildVar",
20+ "ChildVar",
21+ }
22+)
23+
12 // delimToCamel to camel converts a string with parts separated by `delim` to CamelCase24 // delimToCamel to camel converts a string with parts separated by `delim` to CamelCase
13 func delimToCamel(s string, delim string) string {25 func delimToCamel(s string, delim string) string {
14 var sb strings.Builder26 var sb strings.Builder
@@ -123,6 +135,70 @@ func ConvertArgsComma(a []string) string {
123 return ", " + strings.Join(a, ", ")135 return ", " + strings.Join(a, ", ")
124 }136 }
125 137
138+func convertCallbackArgs(a []string, prependComma, skipEmpty, skipErr, handlePtr bool) string {
139+ var validArgs []string
140+ for _, arg := range a {
141+ if skipEmpty && arg == "" {
142+ continue
143+ }
144+ if skipErr && arg == "&cerr" {
145+ continue
146+ }
147+
148+ if strings.Contains(arg, "{Ptr:") {
149+ if !handlePtr {
150+ // For ConvertCallbackArgs: remove * prefix and add &
151+ arg = strings.TrimPrefix(arg, "*")
152+ }
153+ validArgs = append(validArgs, "&"+arg)
154+ } else if strings.Contains(arg, "ConvertPtr(") && handlePtr {
155+ isSpecialVar := false
156+ for _, specialVar := range specialConvertPtrVars {
157+ if strings.Contains(arg, specialVar) {
158+ isSpecialVar = true
159+
160+ break
161+ }
162+ }
163+
164+ if isSpecialVar {
165+ validArgs = append(validArgs, arg)
166+ } else {
167+ validArgs = append(validArgs, "*"+arg)
168+ }
169+ } else {
170+ validArgs = append(validArgs, arg)
171+ }
172+ }
173+
174+ if len(validArgs) == 0 {
175+ return ""
176+ }
177+
178+ result := strings.Join(validArgs, ", ")
179+ if prependComma {
180+ return ", " + result
181+ }
182+
183+ return result
184+}
185+
186+func ConvertCallbackArgs(a []string) string {
187+ return convertCallbackArgs(a, false, false, false, false)
188+}
189+
190+func ConvertArgsCommaDeref(a []string) string {
191+ return convertCallbackArgs(a, true, true, false, true)
192+}
193+
194+func ConvertArgsDeref(a []string) string {
195+ return convertCallbackArgs(a, false, true, false, true)
196+}
197+
198+func ConvertCallbackArgsNoErr(a []string) string {
199+ return convertCallbackArgs(a, false, true, true, true)
200+}
201+
126 // ConstructorName returns a Go friendly constructor name given the raw constructor name `name` and the class/record name `outer`202 // ConstructorName returns a Go friendly constructor name given the raw constructor name `name` and the class/record name `outer`
127 func ConstructorName(name string, outer string) string {203 func ConstructorName(name string, outer string) string {
128 cname := SnakeToCamel(name)204 cname := SnakeToCamel(name)
M pkg/gir/util/util.go +6 -2
@@ -3,6 +3,10 @@ package util
33 import "github.com/jwijenbergh/puregotk/internal/gir/util"
44
55 var (
6- ConvertArgs = util.ConvertArgs
7- ConvertArgsComma = util.ConvertArgsComma
6+ ConvertArgs = util.ConvertArgs
7+ ConvertArgsComma = util.ConvertArgsComma
8+ ConvertCallbackArgs = util.ConvertCallbackArgs
9+ ConvertArgsCommaDeref = util.ConvertArgsCommaDeref
10+ ConvertArgsDeref = util.ConvertArgsDeref
11+ ConvertCallbackArgsNoErr = util.ConvertCallbackArgsNoErr
812 )
@@ -3,6 +3,10 @@ package util
3 import "github.com/jwijenbergh/puregotk/internal/gir/util"3 import "github.com/jwijenbergh/puregotk/internal/gir/util"
4 4
5 var (5 var (
6- ConvertArgs = util.ConvertArgs6+ ConvertArgs = util.ConvertArgs
7- ConvertArgsComma = util.ConvertArgsComma7+ ConvertArgsComma = util.ConvertArgsComma
8+ ConvertCallbackArgs = util.ConvertCallbackArgs
9+ ConvertArgsCommaDeref = util.ConvertArgsCommaDeref
10+ ConvertArgsDeref = util.ConvertArgsDeref
11+ ConvertCallbackArgsNoErr = util.ConvertCallbackArgsNoErr
8 )12 )
M templates/go +46 -9
@@ -66,7 +66,7 @@ var x{{.Name}} func({{conv .Args.Pure.Types}}) {{.Ret.Raw}}
6666 {{.Doc}}
6767 func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
6868 {{.Ret.Preamble $NotGLib}}
69- {{if .Ret.Value}}cret :={{end}}x{{.Name}}({{conv .Args.API.Call}})
69+ {{if .Ret.Value}}cret :={{end}}x{{.Name}}({{convd .Args.API.Call}})
7070 {{.Ret.Fmt $NotGObject}}
7171 }
7272 {{end}}
@@ -78,11 +78,48 @@ var x{{$outer.Name}}{{.Name}} func(uintptr {{convc .Args.Pure.Types}}) {{.Ret.Ra
7878 {{.Doc}}
7979 func (x *{{$outer.Name}}) {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
8080 {{.Ret.Preamble $NotGLib}}
81- {{if .Ret.Value}}cret :={{end}}x{{$outer.Name}}{{.Name}}(x.GoPointer() {{convc .Args.API.Call}})
81+ {{if .Ret.Value}}cret :={{end}}x{{$outer.Name}}{{.Name}}(x.GoPointer() {{convcd .Args.API.Call}})
8282 {{.Ret.Fmt $NotGObject}}
8383 }
8484 {{end}}
8585
86+{{range .CallbackAccessors -}}
87+{{if .Doc}}// SetCallback{{.Name}} sets the callback function.
88+{{.Doc}}{{else}}// SetCallback{{.Name}} sets the callback function.{{end}}
89+func (x *{{$outer.Name}}) SetCallback{{.Name}}(cb {{.CallbackType}}) {
90+ if cb == nil {
91+ x.x{{.Name}} = 0
92+ } else {
93+ x.x{{.Name}} = purego.NewCallback(func({{conv .Args.Pure.Full}}) {{.Ret.Raw}} {
94+ {{if .Ret.Value}}{{if .Ret.Class}}ret := cb({{convcb .Args.Pure.Call}})
95+ if ret == nil {
96+ return 0
97+ }
98+ return ret.GoPointer(){{else}}return cb({{convcb .Args.Pure.Call}}){{end}}{{else}}cb({{convcb .Args.Pure.Call}}){{end}}
99+ })
100+ }
101+}
102+
103+{{if .Doc}}// GetCallback{{.Name}} gets the callback function.
104+{{.Doc}}{{else}}// GetCallback{{.Name}} gets the callback function.{{end}}
105+func (x *{{$outer.Name}}) GetCallback{{.Name}}() {{.CallbackType}} {
106+ if x.x{{.Name}} == 0 {
107+ return nil
108+ }
109+ var rawCallback func({{conv .Args.Pure.Full}}) {{.Ret.Raw}}
110+ purego.RegisterFunc(&rawCallback, x.x{{.Name}})
111+ return func({{conv .Args.API.Full}}) {{.Ret.Value}} {
112+ {{if .Ret.Value}}{{if .Ret.Class}}rawRet := rawCallback({{convcbne .Args.API.Call}})
113+ if rawRet == 0 {
114+ return nil
115+ }
116+ ret := {{.Ret.Instance}}
117+ ret.Ptr = rawRet
118+ return ret{{else}}return rawCallback({{convcbne .Args.API.Call}}){{end}}{{else}}rawCallback({{convcbne .Args.API.Call}}){{end}}
119+ }
120+}
121+{{end}}
122+
86123 {{end}}
87124
88125 {{range .Interfaces -}}
@@ -91,7 +128,7 @@ type {{.Name}} interface {
91128 GoPointer() uintptr
92129 SetGoPointer(uintptr)
93130 {{range .Methods -}}
94- {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Value}}
131+ {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}}
95132 {{end}}
96133 }
97134
@@ -122,7 +159,7 @@ func (x *{{.Name}}Base) SetGoPointer(ptr uintptr) {
122159 {{.Doc}}
123160 func (x *{{$outer.Name}}Base) {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
124161 {{.Ret.Preamble $NotGLib}}
125- {{if .Ret.Value}}cret := {{end}}{{.Namespace}}X{{.FullName}}(x.GoPointer() {{convc .Args.API.Call}})
162+ {{if .Ret.Value}}cret := {{end}}{{.Namespace}}X{{.FullName}}(x.GoPointer() {{convcd .Args.API.Call}})
126163 {{.Ret.Fmt $NotGObject}}
127164 }
128165 {{end}}
@@ -176,7 +213,7 @@ var x{{.Name}} func({{conv .Args.Pure.Types}}) {{.Ret.Raw}}
176213 {{.Doc}}
177214 func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
178215 {{.Ret.Preamble $NotGLib}}
179- {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{conv .Args.API.Call}})
216+ {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.Call}})
180217 {{.Ret.Fmt $NotGObject}}
181218 }
182219 {{end}}
@@ -211,7 +248,7 @@ var x{{.Name}} func({{conv .Args.Pure.Types}}) {{.Ret.Raw}}
211248 {{.Doc}}
212249 func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
213250 {{.Ret.Preamble $NotGLib}}
214- {{if .Ret.Value}}cret :={{end}}x{{.Name}}({{conv .Args.API.Call}})
251+ {{if .Ret.Value}}cret :={{end}}x{{.Name}}({{convd .Args.API.Call}})
215252 {{.Ret.Fmt $NotGObject}}
216253 }
217254 {{end}}
@@ -223,7 +260,7 @@ var x{{$outer.Name}}{{.Name}} func(uintptr {{convc .Args.Pure.Types}}) {{.Ret.Ra
223260 {{.Doc}}
224261 func (x *{{$outer.Name}}) {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
225262 {{.Ret.Preamble $NotGLib}}
226- {{if .Ret.Value}}cret :={{end}}x{{$outer.Name}}{{.Name}}(x.GoPointer() {{convc .Args.API.Call}})
263+ {{if .Ret.Value}}cret :={{end}}x{{$outer.Name}}{{.Name}}(x.GoPointer() {{convcd .Args.API.Call}})
227264 {{.Ret.Fmt $NotGObject}}
228265 }
229266 {{end}}
@@ -272,7 +309,7 @@ func (x *{{$outer.Name}}) Connect{{.Name}}(cb *func({{$outer.Name}} {{convc .Arg
272309 {{.Doc}}
273310 func (x *{{$outer.Name}}) {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
274311 {{.Ret.Preamble $NotGLib}}
275- {{if .Ret.Value}}cret := {{end}} {{.Namespace}}X{{.FullName}}(x.GoPointer() {{convc .Args.API.Call}})
312+ {{if .Ret.Value}}cret := {{end}} {{.Namespace}}X{{.FullName}}(x.GoPointer() {{convcd .Args.API.Call}})
276313 {{.Ret.Fmt $NotGObject}}
277314 }
278315 {{end}}
@@ -284,7 +321,7 @@ var x{{.Name}} func({{conv .Args.Pure.Types}}) {{.Ret.Raw}}
284321 {{.Doc}}
285322 func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
286323 {{.Ret.Preamble $NotGLib}}
287- {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{conv .Args.API.Call}})
324+ {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.Call}})
288325 {{.Ret.Fmt $NotGObject}}
289326 }
290327 {{end}}
@@ -66,7 +66,7 @@ var x{{.Name}} func({{conv .Args.Pure.Types}}) {{.Ret.Raw}}
66 {{.Doc}}66 {{.Doc}}
67 func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {67 func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
68 {{.Ret.Preamble $NotGLib}}68 {{.Ret.Preamble $NotGLib}}
69- {{if .Ret.Value}}cret :={{end}}x{{.Name}}({{conv .Args.API.Call}})69+ {{if .Ret.Value}}cret :={{end}}x{{.Name}}({{convd .Args.API.Call}})
70 {{.Ret.Fmt $NotGObject}}70 {{.Ret.Fmt $NotGObject}}
71 }71 }
72 {{end}}72 {{end}}
@@ -78,11 +78,48 @@ var x{{$outer.Name}}{{.Name}} func(uintptr {{convc .Args.Pure.Types}}) {{.Ret.Ra
78 {{.Doc}}78 {{.Doc}}
79 func (x *{{$outer.Name}}) {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {79 func (x *{{$outer.Name}}) {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
80 {{.Ret.Preamble $NotGLib}}80 {{.Ret.Preamble $NotGLib}}
81- {{if .Ret.Value}}cret :={{end}}x{{$outer.Name}}{{.Name}}(x.GoPointer() {{convc .Args.API.Call}})81+ {{if .Ret.Value}}cret :={{end}}x{{$outer.Name}}{{.Name}}(x.GoPointer() {{convcd .Args.API.Call}})
82 {{.Ret.Fmt $NotGObject}}82 {{.Ret.Fmt $NotGObject}}
83 }83 }
84 {{end}}84 {{end}}
85 85
86+{{range .CallbackAccessors -}}
87+{{if .Doc}}// SetCallback{{.Name}} sets the callback function.
88+{{.Doc}}{{else}}// SetCallback{{.Name}} sets the callback function.{{end}}
89+func (x *{{$outer.Name}}) SetCallback{{.Name}}(cb {{.CallbackType}}) {
90+ if cb == nil {
91+ x.x{{.Name}} = 0
92+ } else {
93+ x.x{{.Name}} = purego.NewCallback(func({{conv .Args.Pure.Full}}) {{.Ret.Raw}} {
94+ {{if .Ret.Value}}{{if .Ret.Class}}ret := cb({{convcb .Args.Pure.Call}})
95+ if ret == nil {
96+ return 0
97+ }
98+ return ret.GoPointer(){{else}}return cb({{convcb .Args.Pure.Call}}){{end}}{{else}}cb({{convcb .Args.Pure.Call}}){{end}}
99+ })
100+ }
101+}
102+
103+{{if .Doc}}// GetCallback{{.Name}} gets the callback function.
104+{{.Doc}}{{else}}// GetCallback{{.Name}} gets the callback function.{{end}}
105+func (x *{{$outer.Name}}) GetCallback{{.Name}}() {{.CallbackType}} {
106+ if x.x{{.Name}} == 0 {
107+ return nil
108+ }
109+ var rawCallback func({{conv .Args.Pure.Full}}) {{.Ret.Raw}}
110+ purego.RegisterFunc(&rawCallback, x.x{{.Name}})
111+ return func({{conv .Args.API.Full}}) {{.Ret.Value}} {
112+ {{if .Ret.Value}}{{if .Ret.Class}}rawRet := rawCallback({{convcbne .Args.API.Call}})
113+ if rawRet == 0 {
114+ return nil
115+ }
116+ ret := {{.Ret.Instance}}
117+ ret.Ptr = rawRet
118+ return ret{{else}}return rawCallback({{convcbne .Args.API.Call}}){{end}}{{else}}rawCallback({{convcbne .Args.API.Call}}){{end}}
119+ }
120+}
121+{{end}}
122+
86 {{end}}123 {{end}}
87 124
88 {{range .Interfaces -}}125 {{range .Interfaces -}}
@@ -91,7 +128,7 @@ type {{.Name}} interface {
91 GoPointer() uintptr128 GoPointer() uintptr
92 SetGoPointer(uintptr)129 SetGoPointer(uintptr)
93 {{range .Methods -}}130 {{range .Methods -}}
94- {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Value}}131+ {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}}
95 {{end}}132 {{end}}
96 }133 }
97 134
@@ -122,7 +159,7 @@ func (x *{{.Name}}Base) SetGoPointer(ptr uintptr) {
122 {{.Doc}}159 {{.Doc}}
123 func (x *{{$outer.Name}}Base) {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {160 func (x *{{$outer.Name}}Base) {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
124 {{.Ret.Preamble $NotGLib}}161 {{.Ret.Preamble $NotGLib}}
125- {{if .Ret.Value}}cret := {{end}}{{.Namespace}}X{{.FullName}}(x.GoPointer() {{convc .Args.API.Call}})162+ {{if .Ret.Value}}cret := {{end}}{{.Namespace}}X{{.FullName}}(x.GoPointer() {{convcd .Args.API.Call}})
126 {{.Ret.Fmt $NotGObject}}163 {{.Ret.Fmt $NotGObject}}
127 }164 }
128 {{end}}165 {{end}}
@@ -176,7 +213,7 @@ var x{{.Name}} func({{conv .Args.Pure.Types}}) {{.Ret.Raw}}
176 {{.Doc}}213 {{.Doc}}
177 func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {214 func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
178 {{.Ret.Preamble $NotGLib}}215 {{.Ret.Preamble $NotGLib}}
179- {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{conv .Args.API.Call}})216+ {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.Call}})
180 {{.Ret.Fmt $NotGObject}}217 {{.Ret.Fmt $NotGObject}}
181 }218 }
182 {{end}}219 {{end}}
@@ -211,7 +248,7 @@ var x{{.Name}} func({{conv .Args.Pure.Types}}) {{.Ret.Raw}}
211 {{.Doc}}248 {{.Doc}}
212 func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {249 func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
213 {{.Ret.Preamble $NotGLib}}250 {{.Ret.Preamble $NotGLib}}
214- {{if .Ret.Value}}cret :={{end}}x{{.Name}}({{conv .Args.API.Call}})251+ {{if .Ret.Value}}cret :={{end}}x{{.Name}}({{convd .Args.API.Call}})
215 {{.Ret.Fmt $NotGObject}}252 {{.Ret.Fmt $NotGObject}}
216 }253 }
217 {{end}}254 {{end}}
@@ -223,7 +260,7 @@ var x{{$outer.Name}}{{.Name}} func(uintptr {{convc .Args.Pure.Types}}) {{.Ret.Ra
223 {{.Doc}}260 {{.Doc}}
224 func (x *{{$outer.Name}}) {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {261 func (x *{{$outer.Name}}) {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
225 {{.Ret.Preamble $NotGLib}}262 {{.Ret.Preamble $NotGLib}}
226- {{if .Ret.Value}}cret :={{end}}x{{$outer.Name}}{{.Name}}(x.GoPointer() {{convc .Args.API.Call}})263+ {{if .Ret.Value}}cret :={{end}}x{{$outer.Name}}{{.Name}}(x.GoPointer() {{convcd .Args.API.Call}})
227 {{.Ret.Fmt $NotGObject}}264 {{.Ret.Fmt $NotGObject}}
228 }265 }
229 {{end}}266 {{end}}
@@ -272,7 +309,7 @@ func (x *{{$outer.Name}}) Connect{{.Name}}(cb *func({{$outer.Name}} {{convc .Arg
272 {{.Doc}}309 {{.Doc}}
273 func (x *{{$outer.Name}}) {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {310 func (x *{{$outer.Name}}) {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
274 {{.Ret.Preamble $NotGLib}}311 {{.Ret.Preamble $NotGLib}}
275- {{if .Ret.Value}}cret := {{end}} {{.Namespace}}X{{.FullName}}(x.GoPointer() {{convc .Args.API.Call}})312+ {{if .Ret.Value}}cret := {{end}} {{.Namespace}}X{{.FullName}}(x.GoPointer() {{convcd .Args.API.Call}})
276 {{.Ret.Fmt $NotGObject}}313 {{.Ret.Fmt $NotGObject}}
277 }314 }
278 {{end}}315 {{end}}
@@ -284,7 +321,7 @@ var x{{.Name}} func({{conv .Args.Pure.Types}}) {{.Ret.Raw}}
284 {{.Doc}}321 {{.Doc}}
285 func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {322 func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
286 {{.Ret.Preamble $NotGLib}}323 {{.Ret.Preamble $NotGLib}}
287- {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{conv .Args.API.Call}})324+ {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.Call}})
288 {{.Ret.Fmt $NotGObject}}325 {{.Ret.Fmt $NotGObject}}
289 }326 }
290 {{end}}327 {{end}}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close