detached fb13c24

Merge branch 'feat/purego-callback-lifecycle' into main

commit
fb13c245f9f0325f695d1f444b54395b8c8519bc
author
brice · · 2026-02-12 18:13 UTC
merge of
b182548 + 02837fa · diff against the first parent
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
6 files · +309 -8
M templates/glib +114 -3
@@ -14,10 +14,14 @@ var callbacks = struct {
1414 refs map[uintptr]uintptr
1515 closures map[uintptr]interface{}
1616 handlerToCallback map[uint]uintptr
17+ sourceToCallback map[uint]uintptr
18+ callbackRefCount map[uintptr]int
1719 }{
1820 refs: make(map[uintptr]uintptr),
1921 closures: make(map[uintptr]interface{}),
2022 handlerToCallback: make(map[uint]uintptr),
23+ sourceToCallback: make(map[uint]uintptr),
24+ callbackRefCount: make(map[uintptr]int),
2125 }
2226
2327 // GetCallback retrives a callback reference by value.
@@ -44,6 +48,9 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{})
4448 callbacks.Lock()
4549 callbacks.refs[cbPtr] = refPtr
4650 callbacks.closures[cbPtr] = closure
51+ if _, ok := callbacks.callbackRefCount[cbPtr]; !ok {
52+ callbacks.callbackRefCount[cbPtr] = 1
53+ }
4754 callbacks.Unlock()
4855 }
4956
@@ -52,26 +59,130 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{})
5259 // Users should not need to call this.
5360 func RemoveCallback(cbPtr uintptr) {
5461 callbacks.Lock()
62+ for handlerID, mappedCbPtr := range callbacks.handlerToCallback {
63+ if mappedCbPtr == cbPtr {
64+ delete(callbacks.handlerToCallback, handlerID)
65+ }
66+ }
67+ for sourceID, mappedCbPtr := range callbacks.sourceToCallback {
68+ if mappedCbPtr == cbPtr {
69+ delete(callbacks.sourceToCallback, sourceID)
70+ }
71+ }
5572 delete(callbacks.refs, cbPtr)
5673 delete(callbacks.closures, cbPtr)
74+ delete(callbacks.callbackRefCount, cbPtr)
5775 callbacks.Unlock()
5876 }
5977
78+// acquireCallbackRef increments callbackRefCount for cbPtr.
79+// Caller must hold callbacks.Lock().
80+func acquireCallbackRef(cbPtr uintptr) {
81+ callbacks.callbackRefCount[cbPtr]++
82+}
83+
84+func hasCallbackMappings(cbPtr uintptr) bool {
85+ for _, mappedCbPtr := range callbacks.handlerToCallback {
86+ if mappedCbPtr == cbPtr {
87+ return true
88+ }
89+ }
90+ for _, mappedCbPtr := range callbacks.sourceToCallback {
91+ if mappedCbPtr == cbPtr {
92+ return true
93+ }
94+ }
95+ return false
96+}
97+
98+// releaseCallbackRef decrements callbackRefCount for cbPtr and removes callback
99+// data when it reaches zero.
100+// Caller must hold callbacks.Lock().
101+// Handler/source mappings to cbPtr are expected to be removed or replaced by
102+// the caller (RemoveCallbackByHandler, RemoveCallbackBySource,
103+// SaveHandlerMapping, SaveSourceMapping).
104+func releaseCallbackRef(cbPtr uintptr) {
105+ count, ok := callbacks.callbackRefCount[cbPtr]
106+ if !ok {
107+ return
108+ }
109+
110+ count--
111+ if count > 0 {
112+ callbacks.callbackRefCount[cbPtr] = count
113+ return
114+ }
115+
116+ delete(callbacks.callbackRefCount, cbPtr)
117+ delete(callbacks.refs, cbPtr)
118+ delete(callbacks.closures, cbPtr)
119+}
120+
60121 // SaveHandlerMapping records a signal handler ID → callback pointer mapping
61122 // so that DisconnectSignal can clean up the callback registry.
62123 func SaveHandlerMapping(handlerID uint, cbPtr uintptr) {
124+ if handlerID == 0 {
125+ return
126+ }
127+
63128 callbacks.Lock()
129+ defer callbacks.Unlock()
130+ if prevCbPtr, ok := callbacks.handlerToCallback[handlerID]; ok {
131+ if prevCbPtr == cbPtr {
132+ return
133+ }
134+ releaseCallbackRef(prevCbPtr)
135+ if !hasCallbackMappings(prevCbPtr) {
136+ releaseCallbackRef(prevCbPtr)
137+ }
138+ }
64139 callbacks.handlerToCallback[handlerID] = cbPtr
65- callbacks.Unlock()
140+ acquireCallbackRef(cbPtr)
66141 }
67142
68143 // RemoveCallbackByHandler removes a callback from the registry using a signal handler ID.
69144 func RemoveCallbackByHandler(handlerID uint) {
70145 callbacks.Lock()
71146 if cbPtr, ok := callbacks.handlerToCallback[handlerID]; ok {
72- delete(callbacks.refs, cbPtr)
73- delete(callbacks.closures, cbPtr)
74147 delete(callbacks.handlerToCallback, handlerID)
148+ releaseCallbackRef(cbPtr)
149+ if !hasCallbackMappings(cbPtr) {
150+ releaseCallbackRef(cbPtr)
151+ }
152+ }
153+ callbacks.Unlock()
154+}
155+
156+// SaveSourceMapping records a source ID -> callback pointer mapping.
157+func SaveSourceMapping(sourceID uint, cbPtr uintptr) {
158+ if sourceID == 0 {
159+ return
160+ }
161+
162+ callbacks.Lock()
163+ defer callbacks.Unlock()
164+ if prevCbPtr, ok := callbacks.sourceToCallback[sourceID]; ok {
165+ if prevCbPtr == cbPtr {
166+ return
167+ }
168+ releaseCallbackRef(prevCbPtr)
169+ if !hasCallbackMappings(prevCbPtr) {
170+ releaseCallbackRef(prevCbPtr)
171+ }
172+ }
173+ callbacks.sourceToCallback[sourceID] = cbPtr
174+ acquireCallbackRef(cbPtr)
175+}
176+
177+// RemoveCallbackBySource removes a callback mapping using a source ID.
178+func RemoveCallbackBySource(sourceID uint) {
179+ callbacks.Lock()
180+ if cbPtr, ok := callbacks.sourceToCallback[sourceID]; ok {
181+ delete(callbacks.sourceToCallback, sourceID)
182+ releaseCallbackRef(cbPtr)
183+ if !hasCallbackMappings(cbPtr) {
184+ releaseCallbackRef(cbPtr)
185+ }
75186 }
76187 callbacks.Unlock()
77188 }
@@ -14,10 +14,14 @@ var callbacks = struct {
14 refs map[uintptr]uintptr14 refs map[uintptr]uintptr
15 closures map[uintptr]interface{}15 closures map[uintptr]interface{}
16 handlerToCallback map[uint]uintptr16 handlerToCallback map[uint]uintptr
17+ sourceToCallback map[uint]uintptr
18+ callbackRefCount map[uintptr]int
17 }{19 }{
18 refs: make(map[uintptr]uintptr),20 refs: make(map[uintptr]uintptr),
19 closures: make(map[uintptr]interface{}),21 closures: make(map[uintptr]interface{}),
20 handlerToCallback: make(map[uint]uintptr),22 handlerToCallback: make(map[uint]uintptr),
23+ sourceToCallback: make(map[uint]uintptr),
24+ callbackRefCount: make(map[uintptr]int),
21 }25 }
22 26
23 // GetCallback retrives a callback reference by value.27 // GetCallback retrives a callback reference by value.
@@ -44,6 +48,9 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{})
44 callbacks.Lock()48 callbacks.Lock()
45 callbacks.refs[cbPtr] = refPtr49 callbacks.refs[cbPtr] = refPtr
46 callbacks.closures[cbPtr] = closure50 callbacks.closures[cbPtr] = closure
51+ if _, ok := callbacks.callbackRefCount[cbPtr]; !ok {
52+ callbacks.callbackRefCount[cbPtr] = 1
53+ }
47 callbacks.Unlock()54 callbacks.Unlock()
48 }55 }
49 56
@@ -52,26 +59,130 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{})
52 // Users should not need to call this.59 // Users should not need to call this.
53 func RemoveCallback(cbPtr uintptr) {60 func RemoveCallback(cbPtr uintptr) {
54 callbacks.Lock()61 callbacks.Lock()
62+ for handlerID, mappedCbPtr := range callbacks.handlerToCallback {
63+ if mappedCbPtr == cbPtr {
64+ delete(callbacks.handlerToCallback, handlerID)
65+ }
66+ }
67+ for sourceID, mappedCbPtr := range callbacks.sourceToCallback {
68+ if mappedCbPtr == cbPtr {
69+ delete(callbacks.sourceToCallback, sourceID)
70+ }
71+ }
55 delete(callbacks.refs, cbPtr)72 delete(callbacks.refs, cbPtr)
56 delete(callbacks.closures, cbPtr)73 delete(callbacks.closures, cbPtr)
74+ delete(callbacks.callbackRefCount, cbPtr)
57 callbacks.Unlock()75 callbacks.Unlock()
58 }76 }
59 77
78+// acquireCallbackRef increments callbackRefCount for cbPtr.
79+// Caller must hold callbacks.Lock().
80+func acquireCallbackRef(cbPtr uintptr) {
81+ callbacks.callbackRefCount[cbPtr]++
82+}
83+
84+func hasCallbackMappings(cbPtr uintptr) bool {
85+ for _, mappedCbPtr := range callbacks.handlerToCallback {
86+ if mappedCbPtr == cbPtr {
87+ return true
88+ }
89+ }
90+ for _, mappedCbPtr := range callbacks.sourceToCallback {
91+ if mappedCbPtr == cbPtr {
92+ return true
93+ }
94+ }
95+ return false
96+}
97+
98+// releaseCallbackRef decrements callbackRefCount for cbPtr and removes callback
99+// data when it reaches zero.
100+// Caller must hold callbacks.Lock().
101+// Handler/source mappings to cbPtr are expected to be removed or replaced by
102+// the caller (RemoveCallbackByHandler, RemoveCallbackBySource,
103+// SaveHandlerMapping, SaveSourceMapping).
104+func releaseCallbackRef(cbPtr uintptr) {
105+ count, ok := callbacks.callbackRefCount[cbPtr]
106+ if !ok {
107+ return
108+ }
109+
110+ count--
111+ if count > 0 {
112+ callbacks.callbackRefCount[cbPtr] = count
113+ return
114+ }
115+
116+ delete(callbacks.callbackRefCount, cbPtr)
117+ delete(callbacks.refs, cbPtr)
118+ delete(callbacks.closures, cbPtr)
119+}
120+
60 // SaveHandlerMapping records a signal handler ID → callback pointer mapping121 // SaveHandlerMapping records a signal handler ID → callback pointer mapping
61 // so that DisconnectSignal can clean up the callback registry.122 // so that DisconnectSignal can clean up the callback registry.
62 func SaveHandlerMapping(handlerID uint, cbPtr uintptr) {123 func SaveHandlerMapping(handlerID uint, cbPtr uintptr) {
124+ if handlerID == 0 {
125+ return
126+ }
127+
63 callbacks.Lock()128 callbacks.Lock()
129+ defer callbacks.Unlock()
130+ if prevCbPtr, ok := callbacks.handlerToCallback[handlerID]; ok {
131+ if prevCbPtr == cbPtr {
132+ return
133+ }
134+ releaseCallbackRef(prevCbPtr)
135+ if !hasCallbackMappings(prevCbPtr) {
136+ releaseCallbackRef(prevCbPtr)
137+ }
138+ }
64 callbacks.handlerToCallback[handlerID] = cbPtr139 callbacks.handlerToCallback[handlerID] = cbPtr
65- callbacks.Unlock()140+ acquireCallbackRef(cbPtr)
66 }141 }
67 142
68 // RemoveCallbackByHandler removes a callback from the registry using a signal handler ID.143 // RemoveCallbackByHandler removes a callback from the registry using a signal handler ID.
69 func RemoveCallbackByHandler(handlerID uint) {144 func RemoveCallbackByHandler(handlerID uint) {
70 callbacks.Lock()145 callbacks.Lock()
71 if cbPtr, ok := callbacks.handlerToCallback[handlerID]; ok {146 if cbPtr, ok := callbacks.handlerToCallback[handlerID]; ok {
72- delete(callbacks.refs, cbPtr)
73- delete(callbacks.closures, cbPtr)
74 delete(callbacks.handlerToCallback, handlerID)147 delete(callbacks.handlerToCallback, handlerID)
148+ releaseCallbackRef(cbPtr)
149+ if !hasCallbackMappings(cbPtr) {
150+ releaseCallbackRef(cbPtr)
151+ }
152+ }
153+ callbacks.Unlock()
154+}
155+
156+// SaveSourceMapping records a source ID -> callback pointer mapping.
157+func SaveSourceMapping(sourceID uint, cbPtr uintptr) {
158+ if sourceID == 0 {
159+ return
160+ }
161+
162+ callbacks.Lock()
163+ defer callbacks.Unlock()
164+ if prevCbPtr, ok := callbacks.sourceToCallback[sourceID]; ok {
165+ if prevCbPtr == cbPtr {
166+ return
167+ }
168+ releaseCallbackRef(prevCbPtr)
169+ if !hasCallbackMappings(prevCbPtr) {
170+ releaseCallbackRef(prevCbPtr)
171+ }
172+ }
173+ callbacks.sourceToCallback[sourceID] = cbPtr
174+ acquireCallbackRef(cbPtr)
175+}
176+
177+// RemoveCallbackBySource removes a callback mapping using a source ID.
178+func RemoveCallbackBySource(sourceID uint) {
179+ callbacks.Lock()
180+ if cbPtr, ok := callbacks.sourceToCallback[sourceID]; ok {
181+ delete(callbacks.sourceToCallback, sourceID)
182+ releaseCallbackRef(cbPtr)
183+ if !hasCallbackMappings(cbPtr) {
184+ releaseCallbackRef(cbPtr)
185+ }
75 }186 }
76 callbacks.Unlock()187 callbacks.Unlock()
77 }188 }
M templates/go +19 -0
@@ -288,6 +288,19 @@ const (
288288 )
289289 {{end}}
290290
291+{{- define "glib_source_mapping_post_hook" -}}
292+{{- if or (eq .Name "ChildWatchAdd") (eq .Name "ChildWatchAddFull") (eq .Name "IdleAdd") (eq .Name "IdleAddFull") (eq .Name "IdleAddOnce") (eq .Name "TimeoutAdd") (eq .Name "TimeoutAddFull") (eq .Name "TimeoutAddOnce") (eq .Name "TimeoutAddSeconds") (eq .Name "TimeoutAddSecondsFull") (eq .Name "TimeoutAddSecondsOnce")}}
293+if FunctionVar != nil {
294+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
295+}
296+{{- end}}
297+{{- if eq .Name "SourceRemove"}}
298+if cret {
299+ RemoveCallbackBySource(TagVar)
300+}
301+{{- end}}
302+{{- end}}
303+
291304 {{range .Functions -}}
292305 var x{{.Name}} func({{conv .Args.Pure.Types}}) {{.Ret.Raw}}
293306
@@ -315,6 +328,9 @@ func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
315328 defer core.GFreeNullable({{.Name}}Ptr)
316329 {{end}}
317330 {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.CallWithRefs}})
331+ {{- if eq $.PkgName "glib"}}
332+ {{template "glib_source_mapping_post_hook" .}}
333+ {{- end}}
318334 {{.Ret.Fmt $NotGObject}}
319335 }
320336 {{end}}
@@ -545,6 +561,9 @@ func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
545561 defer core.GFreeNullable({{.Name}}Ptr)
546562 {{end}}
547563 {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.CallWithRefs}})
564+ {{- if eq $.PkgName "glib"}}
565+ {{template "glib_source_mapping_post_hook" .}}
566+ {{- end}}
548567 {{.Ret.Fmt $NotGObject}}
549568 }
550569 {{end}}
@@ -288,6 +288,19 @@ const (
288 )288 )
289 {{end}}289 {{end}}
290 290
291+{{- define "glib_source_mapping_post_hook" -}}
292+{{- if or (eq .Name "ChildWatchAdd") (eq .Name "ChildWatchAddFull") (eq .Name "IdleAdd") (eq .Name "IdleAddFull") (eq .Name "IdleAddOnce") (eq .Name "TimeoutAdd") (eq .Name "TimeoutAddFull") (eq .Name "TimeoutAddOnce") (eq .Name "TimeoutAddSeconds") (eq .Name "TimeoutAddSecondsFull") (eq .Name "TimeoutAddSecondsOnce")}}
293+if FunctionVar != nil {
294+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
295+}
296+{{- end}}
297+{{- if eq .Name "SourceRemove"}}
298+if cret {
299+ RemoveCallbackBySource(TagVar)
300+}
301+{{- end}}
302+{{- end}}
303+
291 {{range .Functions -}}304 {{range .Functions -}}
292 var x{{.Name}} func({{conv .Args.Pure.Types}}) {{.Ret.Raw}}305 var x{{.Name}} func({{conv .Args.Pure.Types}}) {{.Ret.Raw}}
293 306
@@ -315,6 +328,9 @@ func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
315 defer core.GFreeNullable({{.Name}}Ptr)328 defer core.GFreeNullable({{.Name}}Ptr)
316 {{end}}329 {{end}}
317 {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.CallWithRefs}})330 {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.CallWithRefs}})
331+ {{- if eq $.PkgName "glib"}}
332+ {{template "glib_source_mapping_post_hook" .}}
333+ {{- end}}
318 {{.Ret.Fmt $NotGObject}}334 {{.Ret.Fmt $NotGObject}}
319 }335 }
320 {{end}}336 {{end}}
@@ -545,6 +561,9 @@ func {{.Name}}({{conv .Args.API.Full}}) {{.Ret.Return}} {
545 defer core.GFreeNullable({{.Name}}Ptr)561 defer core.GFreeNullable({{.Name}}Ptr)
546 {{end}}562 {{end}}
547 {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.CallWithRefs}})563 {{if .Ret.Value}}cret := {{end}}x{{.Name}}({{convd .Args.API.CallWithRefs}})
564+ {{- if eq $.PkgName "glib"}}
565+ {{template "glib_source_mapping_post_hook" .}}
566+ {{- end}}
548 {{.Ret.Fmt $NotGObject}}567 {{.Ret.Fmt $NotGObject}}
549 }568 }
550 {{end}}569 {{end}}
M templates/gobject +13 -1
@@ -2,6 +2,7 @@ package gobject
22
33 import (
44 "reflect"
5+ "unsafe"
56
67 "github.com/jwijenbergh/puregotk/v4/glib"
78 )
@@ -37,7 +38,18 @@ func (o Object) Cast(v Ptr) {
3738 }
3839
3940 func (o Object) ConnectSignal(signal string, cb *func()) uint {
40- return SignalConnect(o.GoPointer(), signal, glib.NewCallback(cb))
41+ cbPtr := uintptr(unsafe.Pointer(cb))
42+ if cbRefPtr, ok := glib.GetCallback(cbPtr); ok {
43+ handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr)
44+ glib.SaveHandlerMapping(handlerID, cbPtr)
45+ return handlerID
46+ }
47+
48+ cbRefPtr := glib.NewCallback(cb)
49+ glib.SaveCallbackWithClosure(cbPtr, cbRefPtr, cb)
50+ handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr)
51+ glib.SaveHandlerMapping(handlerID, cbPtr)
52+ return handlerID
4153 }
4254
4355 func (o Object) DisconnectSignal(handler uint) {
@@ -2,6 +2,7 @@ package gobject
2 2
3 import (3 import (
4 "reflect"4 "reflect"
5+ "unsafe"
5 6
6 "github.com/jwijenbergh/puregotk/v4/glib"7 "github.com/jwijenbergh/puregotk/v4/glib"
7 )8 )
@@ -37,7 +38,18 @@ func (o Object) Cast(v Ptr) {
37 }38 }
38 39
39 func (o Object) ConnectSignal(signal string, cb *func()) uint {40 func (o Object) ConnectSignal(signal string, cb *func()) uint {
40- return SignalConnect(o.GoPointer(), signal, glib.NewCallback(cb))41+ cbPtr := uintptr(unsafe.Pointer(cb))
42+ if cbRefPtr, ok := glib.GetCallback(cbPtr); ok {
43+ handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr)
44+ glib.SaveHandlerMapping(handlerID, cbPtr)
45+ return handlerID
46+ }
47+
48+ cbRefPtr := glib.NewCallback(cb)
49+ glib.SaveCallbackWithClosure(cbPtr, cbRefPtr, cb)
50+ handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr)
51+ glib.SaveHandlerMapping(handlerID, cbPtr)
52+ return handlerID
41 }53 }
42 54
43 func (o Object) DisconnectSignal(handler uint) {55 func (o Object) DisconnectSignal(handler uint) {
M v4/glib/gmain.go +36 -0
@@ -1716,6 +1716,9 @@ func ChildWatchAdd(PidVar Pid, FunctionVar *ChildWatchFunc, DataVar uintptr) uin
17161716 }
17171717
17181718 cret := xChildWatchAdd(PidVar, FunctionVarRef, DataVar)
1719+ if FunctionVar != nil {
1720+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
1721+ }
17191722 return cret
17201723 }
17211724
@@ -1779,6 +1782,9 @@ func ChildWatchAddFull(PriorityVar int, PidVar Pid, FunctionVar *ChildWatchFunc,
17791782 }
17801783
17811784 cret := xChildWatchAddFull(PriorityVar, PidVar, FunctionVarRef, DataVar, NotifyVarRef)
1785+ if FunctionVar != nil {
1786+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
1787+ }
17821788 return cret
17831789 }
17841790
@@ -1948,6 +1954,9 @@ func IdleAdd(FunctionVar *SourceFunc, DataVar uintptr) uint {
19481954 }
19491955
19501956 cret := xIdleAdd(FunctionVarRef, DataVar)
1957+ if FunctionVar != nil {
1958+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
1959+ }
19511960 return cret
19521961 }
19531962
@@ -2000,6 +2009,9 @@ func IdleAddFull(PriorityVar int, FunctionVar *SourceFunc, DataVar uintptr, Noti
20002009 }
20012010
20022011 cret := xIdleAddFull(PriorityVar, FunctionVarRef, DataVar, NotifyVarRef)
2012+ if FunctionVar != nil {
2013+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2014+ }
20032015 return cret
20042016 }
20052017
@@ -2033,6 +2045,9 @@ func IdleAddOnce(FunctionVar *SourceOnceFunc, DataVar uintptr) uint {
20332045 }
20342046
20352047 cret := xIdleAddOnce(FunctionVarRef, DataVar)
2048+ if FunctionVar != nil {
2049+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2050+ }
20362051 return cret
20372052 }
20382053
@@ -2266,6 +2281,9 @@ var xSourceRemove func(uint) bool
22662281 func SourceRemove(TagVar uint) bool {
22672282
22682283 cret := xSourceRemove(TagVar)
2284+ if cret {
2285+ RemoveCallbackBySource(TagVar)
2286+ }
22692287 return cret
22702288 }
22712289
@@ -2371,6 +2389,9 @@ func TimeoutAdd(IntervalVar uint, FunctionVar *SourceFunc, DataVar uintptr) uint
23712389 }
23722390
23732391 cret := xTimeoutAdd(IntervalVar, FunctionVarRef, DataVar)
2392+ if FunctionVar != nil {
2393+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2394+ }
23742395 return cret
23752396 }
23762397
@@ -2437,6 +2458,9 @@ func TimeoutAddFull(PriorityVar int, IntervalVar uint, FunctionVar *SourceFunc,
24372458 }
24382459
24392460 cret := xTimeoutAddFull(PriorityVar, IntervalVar, FunctionVarRef, DataVar, NotifyVarRef)
2461+ if FunctionVar != nil {
2462+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2463+ }
24402464 return cret
24412465 }
24422466
@@ -2467,6 +2491,9 @@ func TimeoutAddOnce(IntervalVar uint, FunctionVar *SourceOnceFunc, DataVar uintp
24672491 }
24682492
24692493 cret := xTimeoutAddOnce(IntervalVar, FunctionVarRef, DataVar)
2494+ if FunctionVar != nil {
2495+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2496+ }
24702497 return cret
24712498 }
24722499
@@ -2513,6 +2540,9 @@ func TimeoutAddSeconds(IntervalVar uint, FunctionVar *SourceFunc, DataVar uintpt
25132540 }
25142541
25152542 cret := xTimeoutAddSeconds(IntervalVar, FunctionVarRef, DataVar)
2543+ if FunctionVar != nil {
2544+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2545+ }
25162546 return cret
25172547 }
25182548
@@ -2591,6 +2621,9 @@ func TimeoutAddSecondsFull(PriorityVar int, IntervalVar uint, FunctionVar *Sourc
25912621 }
25922622
25932623 cret := xTimeoutAddSecondsFull(PriorityVar, IntervalVar, FunctionVarRef, DataVar, NotifyVarRef)
2624+ if FunctionVar != nil {
2625+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2626+ }
25942627 return cret
25952628 }
25962629
@@ -2616,6 +2649,9 @@ func TimeoutAddSecondsOnce(IntervalVar uint, FunctionVar *SourceOnceFunc, DataVa
26162649 }
26172650
26182651 cret := xTimeoutAddSecondsOnce(IntervalVar, FunctionVarRef, DataVar)
2652+ if FunctionVar != nil {
2653+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2654+ }
26192655 return cret
26202656 }
26212657
@@ -1716,6 +1716,9 @@ func ChildWatchAdd(PidVar Pid, FunctionVar *ChildWatchFunc, DataVar uintptr) uin
1716 }1716 }
1717 1717
1718 cret := xChildWatchAdd(PidVar, FunctionVarRef, DataVar)1718 cret := xChildWatchAdd(PidVar, FunctionVarRef, DataVar)
1719+ if FunctionVar != nil {
1720+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
1721+ }
1719 return cret1722 return cret
1720 }1723 }
1721 1724
@@ -1779,6 +1782,9 @@ func ChildWatchAddFull(PriorityVar int, PidVar Pid, FunctionVar *ChildWatchFunc,
1779 }1782 }
1780 1783
1781 cret := xChildWatchAddFull(PriorityVar, PidVar, FunctionVarRef, DataVar, NotifyVarRef)1784 cret := xChildWatchAddFull(PriorityVar, PidVar, FunctionVarRef, DataVar, NotifyVarRef)
1785+ if FunctionVar != nil {
1786+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
1787+ }
1782 return cret1788 return cret
1783 }1789 }
1784 1790
@@ -1948,6 +1954,9 @@ func IdleAdd(FunctionVar *SourceFunc, DataVar uintptr) uint {
1948 }1954 }
1949 1955
1950 cret := xIdleAdd(FunctionVarRef, DataVar)1956 cret := xIdleAdd(FunctionVarRef, DataVar)
1957+ if FunctionVar != nil {
1958+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
1959+ }
1951 return cret1960 return cret
1952 }1961 }
1953 1962
@@ -2000,6 +2009,9 @@ func IdleAddFull(PriorityVar int, FunctionVar *SourceFunc, DataVar uintptr, Noti
2000 }2009 }
2001 2010
2002 cret := xIdleAddFull(PriorityVar, FunctionVarRef, DataVar, NotifyVarRef)2011 cret := xIdleAddFull(PriorityVar, FunctionVarRef, DataVar, NotifyVarRef)
2012+ if FunctionVar != nil {
2013+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2014+ }
2003 return cret2015 return cret
2004 }2016 }
2005 2017
@@ -2033,6 +2045,9 @@ func IdleAddOnce(FunctionVar *SourceOnceFunc, DataVar uintptr) uint {
2033 }2045 }
2034 2046
2035 cret := xIdleAddOnce(FunctionVarRef, DataVar)2047 cret := xIdleAddOnce(FunctionVarRef, DataVar)
2048+ if FunctionVar != nil {
2049+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2050+ }
2036 return cret2051 return cret
2037 }2052 }
2038 2053
@@ -2266,6 +2281,9 @@ var xSourceRemove func(uint) bool
2266 func SourceRemove(TagVar uint) bool {2281 func SourceRemove(TagVar uint) bool {
2267 2282
2268 cret := xSourceRemove(TagVar)2283 cret := xSourceRemove(TagVar)
2284+ if cret {
2285+ RemoveCallbackBySource(TagVar)
2286+ }
2269 return cret2287 return cret
2270 }2288 }
2271 2289
@@ -2371,6 +2389,9 @@ func TimeoutAdd(IntervalVar uint, FunctionVar *SourceFunc, DataVar uintptr) uint
2371 }2389 }
2372 2390
2373 cret := xTimeoutAdd(IntervalVar, FunctionVarRef, DataVar)2391 cret := xTimeoutAdd(IntervalVar, FunctionVarRef, DataVar)
2392+ if FunctionVar != nil {
2393+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2394+ }
2374 return cret2395 return cret
2375 }2396 }
2376 2397
@@ -2437,6 +2458,9 @@ func TimeoutAddFull(PriorityVar int, IntervalVar uint, FunctionVar *SourceFunc,
2437 }2458 }
2438 2459
2439 cret := xTimeoutAddFull(PriorityVar, IntervalVar, FunctionVarRef, DataVar, NotifyVarRef)2460 cret := xTimeoutAddFull(PriorityVar, IntervalVar, FunctionVarRef, DataVar, NotifyVarRef)
2461+ if FunctionVar != nil {
2462+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2463+ }
2440 return cret2464 return cret
2441 }2465 }
2442 2466
@@ -2467,6 +2491,9 @@ func TimeoutAddOnce(IntervalVar uint, FunctionVar *SourceOnceFunc, DataVar uintp
2467 }2491 }
2468 2492
2469 cret := xTimeoutAddOnce(IntervalVar, FunctionVarRef, DataVar)2493 cret := xTimeoutAddOnce(IntervalVar, FunctionVarRef, DataVar)
2494+ if FunctionVar != nil {
2495+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2496+ }
2470 return cret2497 return cret
2471 }2498 }
2472 2499
@@ -2513,6 +2540,9 @@ func TimeoutAddSeconds(IntervalVar uint, FunctionVar *SourceFunc, DataVar uintpt
2513 }2540 }
2514 2541
2515 cret := xTimeoutAddSeconds(IntervalVar, FunctionVarRef, DataVar)2542 cret := xTimeoutAddSeconds(IntervalVar, FunctionVarRef, DataVar)
2543+ if FunctionVar != nil {
2544+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2545+ }
2516 return cret2546 return cret
2517 }2547 }
2518 2548
@@ -2591,6 +2621,9 @@ func TimeoutAddSecondsFull(PriorityVar int, IntervalVar uint, FunctionVar *Sourc
2591 }2621 }
2592 2622
2593 cret := xTimeoutAddSecondsFull(PriorityVar, IntervalVar, FunctionVarRef, DataVar, NotifyVarRef)2623 cret := xTimeoutAddSecondsFull(PriorityVar, IntervalVar, FunctionVarRef, DataVar, NotifyVarRef)
2624+ if FunctionVar != nil {
2625+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2626+ }
2594 return cret2627 return cret
2595 }2628 }
2596 2629
@@ -2616,6 +2649,9 @@ func TimeoutAddSecondsOnce(IntervalVar uint, FunctionVar *SourceOnceFunc, DataVa
2616 }2649 }
2617 2650
2618 cret := xTimeoutAddSecondsOnce(IntervalVar, FunctionVarRef, DataVar)2651 cret := xTimeoutAddSecondsOnce(IntervalVar, FunctionVarRef, DataVar)
2652+ if FunctionVar != nil {
2653+ SaveSourceMapping(cret, uintptr(unsafe.Pointer(FunctionVar)))
2654+ }
2619 return cret2655 return cret
2620 }2656 }
2621 2657
M v4/glib/more.go +114 -3
@@ -14,10 +14,14 @@ var callbacks = struct {
1414 refs map[uintptr]uintptr
1515 closures map[uintptr]interface{}
1616 handlerToCallback map[uint]uintptr
17+ sourceToCallback map[uint]uintptr
18+ callbackRefCount map[uintptr]int
1719 }{
1820 refs: make(map[uintptr]uintptr),
1921 closures: make(map[uintptr]interface{}),
2022 handlerToCallback: make(map[uint]uintptr),
23+ sourceToCallback: make(map[uint]uintptr),
24+ callbackRefCount: make(map[uintptr]int),
2125 }
2226
2327 // GetCallback retrives a callback reference by value.
@@ -44,6 +48,9 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{})
4448 callbacks.Lock()
4549 callbacks.refs[cbPtr] = refPtr
4650 callbacks.closures[cbPtr] = closure
51+ if _, ok := callbacks.callbackRefCount[cbPtr]; !ok {
52+ callbacks.callbackRefCount[cbPtr] = 1
53+ }
4754 callbacks.Unlock()
4855 }
4956
@@ -52,26 +59,130 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{})
5259 // Users should not need to call this.
5360 func RemoveCallback(cbPtr uintptr) {
5461 callbacks.Lock()
62+ for handlerID, mappedCbPtr := range callbacks.handlerToCallback {
63+ if mappedCbPtr == cbPtr {
64+ delete(callbacks.handlerToCallback, handlerID)
65+ }
66+ }
67+ for sourceID, mappedCbPtr := range callbacks.sourceToCallback {
68+ if mappedCbPtr == cbPtr {
69+ delete(callbacks.sourceToCallback, sourceID)
70+ }
71+ }
5572 delete(callbacks.refs, cbPtr)
5673 delete(callbacks.closures, cbPtr)
74+ delete(callbacks.callbackRefCount, cbPtr)
5775 callbacks.Unlock()
5876 }
5977
78+// acquireCallbackRef increments callbackRefCount for cbPtr.
79+// Caller must hold callbacks.Lock().
80+func acquireCallbackRef(cbPtr uintptr) {
81+ callbacks.callbackRefCount[cbPtr]++
82+}
83+
84+func hasCallbackMappings(cbPtr uintptr) bool {
85+ for _, mappedCbPtr := range callbacks.handlerToCallback {
86+ if mappedCbPtr == cbPtr {
87+ return true
88+ }
89+ }
90+ for _, mappedCbPtr := range callbacks.sourceToCallback {
91+ if mappedCbPtr == cbPtr {
92+ return true
93+ }
94+ }
95+ return false
96+}
97+
98+// releaseCallbackRef decrements callbackRefCount for cbPtr and removes callback
99+// data when it reaches zero.
100+// Caller must hold callbacks.Lock().
101+// Handler/source mappings to cbPtr are expected to be removed or replaced by
102+// the caller (RemoveCallbackByHandler, RemoveCallbackBySource,
103+// SaveHandlerMapping, SaveSourceMapping).
104+func releaseCallbackRef(cbPtr uintptr) {
105+ count, ok := callbacks.callbackRefCount[cbPtr]
106+ if !ok {
107+ return
108+ }
109+
110+ count--
111+ if count > 0 {
112+ callbacks.callbackRefCount[cbPtr] = count
113+ return
114+ }
115+
116+ delete(callbacks.callbackRefCount, cbPtr)
117+ delete(callbacks.refs, cbPtr)
118+ delete(callbacks.closures, cbPtr)
119+}
120+
60121 // SaveHandlerMapping records a signal handler ID → callback pointer mapping
61122 // so that DisconnectSignal can clean up the callback registry.
62123 func SaveHandlerMapping(handlerID uint, cbPtr uintptr) {
124+ if handlerID == 0 {
125+ return
126+ }
127+
63128 callbacks.Lock()
129+ defer callbacks.Unlock()
130+ if prevCbPtr, ok := callbacks.handlerToCallback[handlerID]; ok {
131+ if prevCbPtr == cbPtr {
132+ return
133+ }
134+ releaseCallbackRef(prevCbPtr)
135+ if !hasCallbackMappings(prevCbPtr) {
136+ releaseCallbackRef(prevCbPtr)
137+ }
138+ }
64139 callbacks.handlerToCallback[handlerID] = cbPtr
65- callbacks.Unlock()
140+ acquireCallbackRef(cbPtr)
66141 }
67142
68143 // RemoveCallbackByHandler removes a callback from the registry using a signal handler ID.
69144 func RemoveCallbackByHandler(handlerID uint) {
70145 callbacks.Lock()
71146 if cbPtr, ok := callbacks.handlerToCallback[handlerID]; ok {
72- delete(callbacks.refs, cbPtr)
73- delete(callbacks.closures, cbPtr)
74147 delete(callbacks.handlerToCallback, handlerID)
148+ releaseCallbackRef(cbPtr)
149+ if !hasCallbackMappings(cbPtr) {
150+ releaseCallbackRef(cbPtr)
151+ }
152+ }
153+ callbacks.Unlock()
154+}
155+
156+// SaveSourceMapping records a source ID -> callback pointer mapping.
157+func SaveSourceMapping(sourceID uint, cbPtr uintptr) {
158+ if sourceID == 0 {
159+ return
160+ }
161+
162+ callbacks.Lock()
163+ defer callbacks.Unlock()
164+ if prevCbPtr, ok := callbacks.sourceToCallback[sourceID]; ok {
165+ if prevCbPtr == cbPtr {
166+ return
167+ }
168+ releaseCallbackRef(prevCbPtr)
169+ if !hasCallbackMappings(prevCbPtr) {
170+ releaseCallbackRef(prevCbPtr)
171+ }
172+ }
173+ callbacks.sourceToCallback[sourceID] = cbPtr
174+ acquireCallbackRef(cbPtr)
175+}
176+
177+// RemoveCallbackBySource removes a callback mapping using a source ID.
178+func RemoveCallbackBySource(sourceID uint) {
179+ callbacks.Lock()
180+ if cbPtr, ok := callbacks.sourceToCallback[sourceID]; ok {
181+ delete(callbacks.sourceToCallback, sourceID)
182+ releaseCallbackRef(cbPtr)
183+ if !hasCallbackMappings(cbPtr) {
184+ releaseCallbackRef(cbPtr)
185+ }
75186 }
76187 callbacks.Unlock()
77188 }
@@ -14,10 +14,14 @@ var callbacks = struct {
14 refs map[uintptr]uintptr14 refs map[uintptr]uintptr
15 closures map[uintptr]interface{}15 closures map[uintptr]interface{}
16 handlerToCallback map[uint]uintptr16 handlerToCallback map[uint]uintptr
17+ sourceToCallback map[uint]uintptr
18+ callbackRefCount map[uintptr]int
17 }{19 }{
18 refs: make(map[uintptr]uintptr),20 refs: make(map[uintptr]uintptr),
19 closures: make(map[uintptr]interface{}),21 closures: make(map[uintptr]interface{}),
20 handlerToCallback: make(map[uint]uintptr),22 handlerToCallback: make(map[uint]uintptr),
23+ sourceToCallback: make(map[uint]uintptr),
24+ callbackRefCount: make(map[uintptr]int),
21 }25 }
22 26
23 // GetCallback retrives a callback reference by value.27 // GetCallback retrives a callback reference by value.
@@ -44,6 +48,9 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{})
44 callbacks.Lock()48 callbacks.Lock()
45 callbacks.refs[cbPtr] = refPtr49 callbacks.refs[cbPtr] = refPtr
46 callbacks.closures[cbPtr] = closure50 callbacks.closures[cbPtr] = closure
51+ if _, ok := callbacks.callbackRefCount[cbPtr]; !ok {
52+ callbacks.callbackRefCount[cbPtr] = 1
53+ }
47 callbacks.Unlock()54 callbacks.Unlock()
48 }55 }
49 56
@@ -52,26 +59,130 @@ func SaveCallbackWithClosure(cbPtr uintptr, refPtr uintptr, closure interface{})
52 // Users should not need to call this.59 // Users should not need to call this.
53 func RemoveCallback(cbPtr uintptr) {60 func RemoveCallback(cbPtr uintptr) {
54 callbacks.Lock()61 callbacks.Lock()
62+ for handlerID, mappedCbPtr := range callbacks.handlerToCallback {
63+ if mappedCbPtr == cbPtr {
64+ delete(callbacks.handlerToCallback, handlerID)
65+ }
66+ }
67+ for sourceID, mappedCbPtr := range callbacks.sourceToCallback {
68+ if mappedCbPtr == cbPtr {
69+ delete(callbacks.sourceToCallback, sourceID)
70+ }
71+ }
55 delete(callbacks.refs, cbPtr)72 delete(callbacks.refs, cbPtr)
56 delete(callbacks.closures, cbPtr)73 delete(callbacks.closures, cbPtr)
74+ delete(callbacks.callbackRefCount, cbPtr)
57 callbacks.Unlock()75 callbacks.Unlock()
58 }76 }
59 77
78+// acquireCallbackRef increments callbackRefCount for cbPtr.
79+// Caller must hold callbacks.Lock().
80+func acquireCallbackRef(cbPtr uintptr) {
81+ callbacks.callbackRefCount[cbPtr]++
82+}
83+
84+func hasCallbackMappings(cbPtr uintptr) bool {
85+ for _, mappedCbPtr := range callbacks.handlerToCallback {
86+ if mappedCbPtr == cbPtr {
87+ return true
88+ }
89+ }
90+ for _, mappedCbPtr := range callbacks.sourceToCallback {
91+ if mappedCbPtr == cbPtr {
92+ return true
93+ }
94+ }
95+ return false
96+}
97+
98+// releaseCallbackRef decrements callbackRefCount for cbPtr and removes callback
99+// data when it reaches zero.
100+// Caller must hold callbacks.Lock().
101+// Handler/source mappings to cbPtr are expected to be removed or replaced by
102+// the caller (RemoveCallbackByHandler, RemoveCallbackBySource,
103+// SaveHandlerMapping, SaveSourceMapping).
104+func releaseCallbackRef(cbPtr uintptr) {
105+ count, ok := callbacks.callbackRefCount[cbPtr]
106+ if !ok {
107+ return
108+ }
109+
110+ count--
111+ if count > 0 {
112+ callbacks.callbackRefCount[cbPtr] = count
113+ return
114+ }
115+
116+ delete(callbacks.callbackRefCount, cbPtr)
117+ delete(callbacks.refs, cbPtr)
118+ delete(callbacks.closures, cbPtr)
119+}
120+
60 // SaveHandlerMapping records a signal handler ID → callback pointer mapping121 // SaveHandlerMapping records a signal handler ID → callback pointer mapping
61 // so that DisconnectSignal can clean up the callback registry.122 // so that DisconnectSignal can clean up the callback registry.
62 func SaveHandlerMapping(handlerID uint, cbPtr uintptr) {123 func SaveHandlerMapping(handlerID uint, cbPtr uintptr) {
124+ if handlerID == 0 {
125+ return
126+ }
127+
63 callbacks.Lock()128 callbacks.Lock()
129+ defer callbacks.Unlock()
130+ if prevCbPtr, ok := callbacks.handlerToCallback[handlerID]; ok {
131+ if prevCbPtr == cbPtr {
132+ return
133+ }
134+ releaseCallbackRef(prevCbPtr)
135+ if !hasCallbackMappings(prevCbPtr) {
136+ releaseCallbackRef(prevCbPtr)
137+ }
138+ }
64 callbacks.handlerToCallback[handlerID] = cbPtr139 callbacks.handlerToCallback[handlerID] = cbPtr
65- callbacks.Unlock()140+ acquireCallbackRef(cbPtr)
66 }141 }
67 142
68 // RemoveCallbackByHandler removes a callback from the registry using a signal handler ID.143 // RemoveCallbackByHandler removes a callback from the registry using a signal handler ID.
69 func RemoveCallbackByHandler(handlerID uint) {144 func RemoveCallbackByHandler(handlerID uint) {
70 callbacks.Lock()145 callbacks.Lock()
71 if cbPtr, ok := callbacks.handlerToCallback[handlerID]; ok {146 if cbPtr, ok := callbacks.handlerToCallback[handlerID]; ok {
72- delete(callbacks.refs, cbPtr)
73- delete(callbacks.closures, cbPtr)
74 delete(callbacks.handlerToCallback, handlerID)147 delete(callbacks.handlerToCallback, handlerID)
148+ releaseCallbackRef(cbPtr)
149+ if !hasCallbackMappings(cbPtr) {
150+ releaseCallbackRef(cbPtr)
151+ }
152+ }
153+ callbacks.Unlock()
154+}
155+
156+// SaveSourceMapping records a source ID -> callback pointer mapping.
157+func SaveSourceMapping(sourceID uint, cbPtr uintptr) {
158+ if sourceID == 0 {
159+ return
160+ }
161+
162+ callbacks.Lock()
163+ defer callbacks.Unlock()
164+ if prevCbPtr, ok := callbacks.sourceToCallback[sourceID]; ok {
165+ if prevCbPtr == cbPtr {
166+ return
167+ }
168+ releaseCallbackRef(prevCbPtr)
169+ if !hasCallbackMappings(prevCbPtr) {
170+ releaseCallbackRef(prevCbPtr)
171+ }
172+ }
173+ callbacks.sourceToCallback[sourceID] = cbPtr
174+ acquireCallbackRef(cbPtr)
175+}
176+
177+// RemoveCallbackBySource removes a callback mapping using a source ID.
178+func RemoveCallbackBySource(sourceID uint) {
179+ callbacks.Lock()
180+ if cbPtr, ok := callbacks.sourceToCallback[sourceID]; ok {
181+ delete(callbacks.sourceToCallback, sourceID)
182+ releaseCallbackRef(cbPtr)
183+ if !hasCallbackMappings(cbPtr) {
184+ releaseCallbackRef(cbPtr)
185+ }
75 }186 }
76 callbacks.Unlock()187 callbacks.Unlock()
77 }188 }
M v4/gobject/more.go +13 -1
@@ -2,6 +2,7 @@ package gobject
22
33 import (
44 "reflect"
5+ "unsafe"
56
67 "github.com/jwijenbergh/puregotk/v4/glib"
78 )
@@ -37,7 +38,18 @@ func (o Object) Cast(v Ptr) {
3738 }
3839
3940 func (o Object) ConnectSignal(signal string, cb *func()) uint {
40- return SignalConnect(o.GoPointer(), signal, glib.NewCallback(cb))
41+ cbPtr := uintptr(unsafe.Pointer(cb))
42+ if cbRefPtr, ok := glib.GetCallback(cbPtr); ok {
43+ handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr)
44+ glib.SaveHandlerMapping(handlerID, cbPtr)
45+ return handlerID
46+ }
47+
48+ cbRefPtr := glib.NewCallback(cb)
49+ glib.SaveCallbackWithClosure(cbPtr, cbRefPtr, cb)
50+ handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr)
51+ glib.SaveHandlerMapping(handlerID, cbPtr)
52+ return handlerID
4153 }
4254
4355 func (o Object) DisconnectSignal(handler uint) {
@@ -2,6 +2,7 @@ package gobject
2 2
3 import (3 import (
4 "reflect"4 "reflect"
5+ "unsafe"
5 6
6 "github.com/jwijenbergh/puregotk/v4/glib"7 "github.com/jwijenbergh/puregotk/v4/glib"
7 )8 )
@@ -37,7 +38,18 @@ func (o Object) Cast(v Ptr) {
37 }38 }
38 39
39 func (o Object) ConnectSignal(signal string, cb *func()) uint {40 func (o Object) ConnectSignal(signal string, cb *func()) uint {
40- return SignalConnect(o.GoPointer(), signal, glib.NewCallback(cb))41+ cbPtr := uintptr(unsafe.Pointer(cb))
42+ if cbRefPtr, ok := glib.GetCallback(cbPtr); ok {
43+ handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr)
44+ glib.SaveHandlerMapping(handlerID, cbPtr)
45+ return handlerID
46+ }
47+
48+ cbRefPtr := glib.NewCallback(cb)
49+ glib.SaveCallbackWithClosure(cbPtr, cbRefPtr, cb)
50+ handlerID := SignalConnect(o.GoPointer(), signal, cbRefPtr)
51+ glib.SaveHandlerMapping(handlerID, cbPtr)
52+ return handlerID
41 }53 }
42 54
43 func (o Object) DisconnectSignal(handler uint) {55 func (o Object) DisconnectSignal(handler uint) {

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