Complete the Delegates wrapper. Disambiguate polymorphic methods.

Bluetooth example is now a functioning heart rate monitor. Additional
bug fixes in Go types (instances where *Id needs to be used instead
of NSObject).
This commit is contained in:
Greg 2019-05-10 02:00:56 -04:00
parent 0de9249e6a
commit dcb7105fca
4 changed files with 136 additions and 29 deletions

View File

@ -16,6 +16,10 @@ func shouldTerminate(s *ns.NSApplication) ns.NSApplicationTerminateReply {
return ns.NSTerminateNow return ns.NSTerminateNow
} }
func shouldTerminateAfterLastWindowClosed(s *ns.NSApplication) ns.BOOL {
return 1
}
func willTerminate(n *ns.NSNotification) { func willTerminate(n *ns.NSNotification) {
fmt.Println("Go: will terminate") fmt.Println("Go: will terminate")
} }
@ -34,6 +38,7 @@ func app() {
del := ns.AppDelegateAlloc() del := ns.AppDelegateAlloc()
del.ApplicationDidFinishLaunchingCallback(didFinishLaunching) del.ApplicationDidFinishLaunchingCallback(didFinishLaunching)
del.ApplicationShouldTerminateCallback(shouldTerminate) del.ApplicationShouldTerminateCallback(shouldTerminate)
del.ApplicationShouldTerminateAfterLastWindowClosedCallback(shouldTerminateAfterLastWindowClosed)
del.ApplicationWillTerminateCallback(willTerminate) del.ApplicationWillTerminateCallback(willTerminate)
del.ApplicationDidBecomeActiveCallback(didBecomeActive) del.ApplicationDidBecomeActiveCallback(didBecomeActive)
a.SetDelegate(del) a.SetDelegate(del)
@ -41,7 +46,7 @@ func app() {
//Set up an NSWindow //Set up an NSWindow
w := ns.NSWindowAlloc().InitWithContentRect( w := ns.NSWindowAlloc().InitWithContentRect(
ns.NSMakeRect(200,200,600,600), ns.NSMakeRect(200,200,600,600),
ns.NSWindowStyleMaskTitled, ns.NSWindowStyleMaskTitled | ns.NSWindowStyleMaskClosable,
ns.NSBackingStoreBuffered, ns.NSBackingStoreBuffered,
0, 0,
nil, nil,

View File

@ -3,7 +3,9 @@ package main
import "C" import "C"
import ( import (
"encoding/binary"
"fmt" "fmt"
"unsafe"
"gitlab.wow.st/gmp/nswrap/examples/bluetooth/ns" "gitlab.wow.st/gmp/nswrap/examples/bluetooth/ns"
) )
@ -22,23 +24,109 @@ func updateState(c *ns.CBCentralManager) {
fmt.Printf(" powered off\n") fmt.Printf(" powered off\n")
case ns.CBManagerStatePoweredOn: case ns.CBManagerStatePoweredOn:
fmt.Printf(" powered on\n") fmt.Printf(" powered on\n")
cm.ScanForPeripheralsWithServices(ns.NSArrayWithObjects(hrm_uuid),nil)
}
}
func discoverPeripheral(c *ns.CBCentralManager, p *ns.CBPeripheral, d *ns.NSDictionary, rssi *ns.NSNumber) {
fmt.Printf("Did discover peripheral\n")
c.StopScan()
peripheral = p
peripheral.Retain()
c.ConnectPeripheral(p,nil)
}
func connectPeripheral(c *ns.CBCentralManager, p *ns.CBPeripheral) {
fmt.Printf("Did connect peripheral\n")
p.SetDelegate(cd)
p.DiscoverServices(nil)
}
func discoverServices(p *ns.CBPeripheral, e *ns.NSError) {
fmt.Printf("Did discover services\n")
p.Services().ObjectEnumerator().ForIn(func(o *ns.Id) bool {
serv := o.CBService()
switch {
case serv.UUID().IsEqualTo(hrm_uuid):
fmt.Printf("--heart rate monitor service\n")
p.DiscoverCharacteristics(nil,serv)
case serv.UUID().IsEqualTo(ns.CBUUIDWithGoString("180A")):
fmt.Printf("--device information service\n")
p.DiscoverCharacteristics(nil,serv)
}
return true
})
}
func hr(d *ns.NSData) int {
var x []byte
if l := int(d.Length()); l < 4 {
return 0
} else {
bs := d.Bytes()
x = make([]byte,l)
for i := 0; i < l; i++ {
x[i] = *(*byte)(unsafe.Pointer(uintptr(bs) + uintptr(i)))
}
}
var hr int
flags := x[0]
if flags & 0x80 != 0 { // uint16 format
hr = int(binary.BigEndian.Uint16(x[1:2]))
} else {
hr = int(x[1])
}
return hr
}
func discoverCharacteristics(p *ns.CBPeripheral, s *ns.CBService, e *ns.NSError) {
fmt.Printf("Did discover characteristics\n")
fmt.Printf("----%s\n",s.UUID().UUIDString().UTF8String())
if s.UUID().IsEqualTo(hrm_uuid) {
s.Characteristics().ObjectEnumerator().ForIn(func(o *ns.Id) bool {
chr := o.CBCharacteristic()
fmt.Printf("------%s\n",chr.UUID().UUIDString().UTF8String())
if chr.UUID().IsEqualTo(hrv_uuid) {
p.SetNotifyValue(1,chr)
v := chr.Value()
fmt.Println(hr(v))
}
return true
})
}
}
func updateValue(p *ns.CBPeripheral, chr *ns.CBCharacteristic, e *ns.NSError) {
if chr.UUID().IsEqualTo(hrv_uuid) {
v := chr.Value()
fmt.Printf("Heart rate: %d\n",hr(v))
} }
} }
var ( var (
hrm_uuid *ns.CBUUID
hrv_uuid *ns.CBUUID
cd *ns.BleDelegate
cm *ns.CBCentralManager cm *ns.CBCentralManager
peripheral *ns.CBPeripheral
) )
func main() { func main() {
queue := ns.DispatchQueueCreate(ns.CharWithGoString("st.wow.gitlab.ble"),nil) queue := ns.DispatchQueueCreate(ns.CharWithGoString("st.wow.gitlab.ble"),nil)
cd := ns.BleDelegateAlloc() cd = ns.BleDelegateAlloc()
cd.CentralManagerDidUpdateStateCallback(updateState) cd.CentralManagerDidUpdateStateCallback(updateState)
cd.CentralManagerDidDiscoverPeripheralCallback(discoverPeripheral)
cd.CentralManagerDidConnectPeripheralCallback(connectPeripheral)
cd.PeripheralDidDiscoverServicesCallback(discoverServices)
cd.PeripheralDidDiscoverCharacteristicsForServiceCallback(discoverCharacteristics)
cd.PeripheralDidUpdateValueForCharacteristicCallback(updateValue)
hrm_uuid = ns.CBUUIDWithGoString("180D")
hrv_uuid = ns.CBUUIDWithGoString("2A37")
cm = ns.CBCentralManagerAlloc() cm = ns.CBCentralManagerAlloc()
cm.InitWithDelegate(cd,queue,nil) cm.InitWithDelegate(cd,queue,nil)
uuid := ns.CBUUIDWithGoString("180d")
_ = uuid
select { } select { }
} }

View File

@ -3,26 +3,24 @@ inputfiles:
- /System/Library/Frameworks/CoreBluetooth.framework/Headers/CoreBluetooth.h - /System/Library/Frameworks/CoreBluetooth.framework/Headers/CoreBluetooth.h
classes: classes:
- ble_delegate - NSObject
- NSData
- CBManager - CBManager
- CBCentralManager - CBCentralManager
- CBPeripheralManager - CBPeripheralManager
- CBPeripheral - CBPeripheral
- CBCentral - CBCentral
- CBService - CBService
- CBAttribute
- CBCharacteristic - CBCharacteristic
- CBDescriptor - CBDescriptor
- CBError - CBError
- CBUUID - CBUUID
- CBAdvertisementData - CBAdvertisementData
- CBATTRequest
- NSArray - NSArray
- NSMutableArray - NSMutableArray
- NSDictionary - NSDictionary
- NSEnumerator - NSEnumerator
- NSSet
- NSDate
- NSTimeZone
- NSString - NSString
functions: [ NSMakeRange, dispatch_queue_create ] functions: [ NSMakeRange, dispatch_queue_create ]

View File

@ -26,6 +26,7 @@ type Wrapper struct {
Protocols map[string]*Protocol Protocols map[string]*Protocol
cgoFlags strings.Builder // put cGo directives here cgoFlags strings.Builder // put cGo directives here
cImports strings.Builder // put c imports and sysimports here
cCode strings.Builder // put cGo code here cCode strings.Builder // put cGo code here
goTypes strings.Builder // put Go type declarations here goTypes strings.Builder // put Go type declarations here
goConst strings.Builder // put Go constants (from C enums) here goConst strings.Builder // put Go constants (from C enums) here
@ -63,20 +64,20 @@ func (w *Wrapper) Frameworks(ss []string) {
return return
} }
for _,s := range ss { for _,s := range ss {
w.cgoFlags.WriteString(fmt.Sprintf("#import <%s/%s.h>\n",s,s)) w.cImports.WriteString(fmt.Sprintf("#import <%s/%s.h>\n",s,s))
} }
w.cgoFlags.WriteString("#cgo LDFLAGS: -framework " + strings.Join(ss," -framework ")) w.cgoFlags.WriteString("#cgo LDFLAGS: -framework " + strings.Join(ss," -framework "))
} }
func (w *Wrapper) Import(ss []string) { func (w *Wrapper) Import(ss []string) {
for _,s := range ss { for _,s := range ss {
w.cgoFlags.WriteString("\n#import \"" + s + "\"\n") w.cImports.WriteString("\n#import \"" + s + "\"\n")
} }
} }
func (w *Wrapper) SysImport(ss []string) { func (w *Wrapper) SysImport(ss []string) {
for _,s := range ss { for _,s := range ss {
w.cgoFlags.WriteString("\n#import <" + s + ">\n") w.cImports.WriteString("\n#import <" + s + ">\n")
} }
} }
@ -116,7 +117,7 @@ type Enum struct {
type Protocol struct { type Protocol struct {
Name, GoName string Name, GoName string
Methods map[string]*Method Methods map[string]*Method
Polymorphic map[string]bool Polymorphic map[string]int
} }
//isVoid() returns true if the method has no return value. //isVoid() returns true if the method has no return value.
@ -291,7 +292,7 @@ func (w *Wrapper) AddProtocol(n *ast.ObjCProtocolDecl) {
Name: n.Name, Name: n.Name,
GoName: types.NewTypeFromString(n.Name,n.Name).GoType(), GoName: types.NewTypeFromString(n.Name,n.Name).GoType(),
Methods: map[string]*Method{}, Methods: map[string]*Method{},
Polymorphic: map[string]bool{}, Polymorphic: map[string]int{},
} }
} }
for _,c := range n.Children() { for _,c := range n.Children() {
@ -307,10 +308,19 @@ func (w *Wrapper) AddProtocol(n *ast.ObjCProtocolDecl) {
var avail bool var avail bool
m.Parameters, avail = w.GetParms(x,p.Name) m.Parameters, avail = w.GetParms(x,p.Name)
if avail { if avail {
if p.Methods[m.Name] != nil { pname := strings.Title(m.Name)
p.Polymorphic[m.Name] = true if x := p.Polymorphic[m.Name]; x != 0 {
p.Polymorphic[m.Name] = x + 1
pname = pname + strings.Title(m.Parameters[1].Pname)
if m2 := p.Methods[m.Name]; m2 != nil {
pname2 := strings.Title(m.Name) + strings.Title(m2.Parameters[1].Pname)
p.Methods[pname2] = m2
delete(p.Methods,m.Name)
}
} else {
p.Polymorphic[m.Name] = 1
} }
p.Methods[m.Name] = m p.Methods[pname] = m
} }
} }
} }
@ -862,9 +872,6 @@ const %s %s= C.%s
} }
} }
//FIXME: need to disambiguate polymorphic method names. Something like:
//func Disambiguate([]*Method) []*Method {} ...
//Can allow user to decide on a disambiguation strategy
//NOTE: The delegate wrapper does not support variadic callback functions. //NOTE: The delegate wrapper does not support variadic callback functions.
func (w *Wrapper) ProcessDelegate(dname string, ps []string) { func (w *Wrapper) ProcessDelegate(dname string, ps []string) {
//To create (per delegate): //To create (per delegate):
@ -884,6 +891,7 @@ func (w *Wrapper) ProcessDelegate(dname string, ps []string) {
//set up array of methods for this delegate //set up array of methods for this delegate
methods := []*Method{} methods := []*Method{}
gnames := []string{} // go names for methods
for _,pname := range ps { for _,pname := range ps {
proto := w.Protocols[pname] proto := w.Protocols[pname]
if proto == nil { if proto == nil {
@ -891,18 +899,16 @@ func (w *Wrapper) ProcessDelegate(dname string, ps []string) {
os.Exit(-1) os.Exit(-1)
} }
fmt.Printf(" proto %s\n",pname) fmt.Printf(" proto %s\n",pname)
for _,m := range proto.Methods { for gname,m := range proto.Methods {
if proto.Polymorphic[m.Name] { if m.Type.IsFunction() || m.Type.IsFunctionPtr() || m.hasFunctionParam() {
//FIXME: skip polymorphic methods for now
fmt.Printf(" Skipping polymorphic method '%s'\n",m.Name)
continue continue
} }
methods = append(methods,m) methods = append(methods,m)
gnames = append(gnames,gname)
} }
} }
methprotos := make([]string,len(methods)) // objc method prototypes methprotos := make([]string,len(methods)) // objc method prototypes
gnames := make([]string,len(methods)) // go names for methods
gname := strings.Title(dname) // go name for this Delegate gname := strings.Title(dname) // go name for this Delegate
vnames := make([][]string,len(methods)) // objc variable names vnames := make([][]string,len(methods)) // objc variable names
gtypes := make([][]string,len(methods)) // go parameter types for each method gtypes := make([][]string,len(methods)) // go parameter types for each method
@ -952,7 +958,6 @@ func (w *Wrapper) ProcessDelegate(dname string, ps []string) {
} }
methprotos[i] = fmt.Sprintf( methprotos[i] = fmt.Sprintf(
`- (%s)%s%s;`,m.Type.Node.Ctype(),m.Name,parms) `- (%s)%s%s;`,m.Type.Node.Ctype(),m.Name,parms)
gnames[i] = strings.Title(m.Name)
if x := m.Type.GoType(); x == "Void" { if x := m.Type.GoType(); x == "Void" {
grtypes[i] = "" grtypes[i] = ""
} else { } else {
@ -1061,8 +1066,12 @@ func (d *%s) %sCallback(f func(%s)%s) {
garglist := []string{} garglist := []string{}
for j := 1; j < len(vnames[i]); j++ { for j := 1; j < len(vnames[i]); j++ {
earglist = append(earglist,vnames[i][j] + " " + getypes[i][j]) earglist = append(earglist,vnames[i][j] + " " + getypes[i][j])
gt2 := gtypes[i][j-1]
if types.IsGoInterface(gt2) {
gt2 = "*Id"
}
garglist = append(garglist,fmt.Sprintf( garglist = append(garglist,fmt.Sprintf(
`(%s)(%s)`,gtypes[i][j-1],vnames[i][j])) `(%s)(%s)`,gt2,vnames[i][j]))
} }
retdecl := "" retdecl := ""
retname := "" retname := ""
@ -1090,7 +1099,6 @@ func %s%s(%s)%s {
} }
`,gname,gnames[i],gname,gnames[i],strings.Join(earglist,", "),crtype,retdecl,gname,gnames[i],retname,retn,strings.Join(garglist,", "),retnparen)) `,gname,gnames[i],gname,gnames[i],strings.Join(earglist,", "),crtype,retdecl,gname,gnames[i],retname,retn,strings.Join(garglist,", "),retnparen))
} }
//DEBUG
w.cCode.WriteString(cprotos.String()) w.cCode.WriteString(cprotos.String())
w.cCode.WriteString(ccode.String()) w.cCode.WriteString(ccode.String())
w.goTypes.WriteString(gotypes.String()) w.goTypes.WriteString(gotypes.String())
@ -1098,6 +1106,7 @@ func %s%s(%s)%s {
w.goExports.WriteString(goexports.String()) w.goExports.WriteString(goexports.String())
} }
func (w *Wrapper) Wrap(toproc []string) { func (w *Wrapper) Wrap(toproc []string) {
if w.Package == "" { w.Package = "ns" } if w.Package == "" { w.Package = "ns" }
err := os.MkdirAll(w.Package,0755) err := os.MkdirAll(w.Package,0755)
@ -1125,6 +1134,11 @@ func (w *Wrapper) Wrap(toproc []string) {
if i == nil { if i == nil {
continue continue
} }
w.processType(types.NewTypeFromString(i.GoName,""))
gname := i.GoName
if types.IsGoInterface(i.GoName) {
gname = "Id"
}
fmt.Printf("Interface %s: %d properties, %d methods\n", fmt.Printf("Interface %s: %d properties, %d methods\n",
i.Name, len(i.Properties), len(i.Methods)) i.Name, len(i.Properties), len(i.Methods))
@ -1132,7 +1146,7 @@ func (w *Wrapper) Wrap(toproc []string) {
func %sAlloc() *%s { func %sAlloc() *%s {
return (*%s)(unsafe.Pointer(C.%sAlloc())) return (*%s)(unsafe.Pointer(C.%sAlloc()))
} }
`,i.GoName,i.GoName,i.GoName,i.Name)) `,i.GoName,gname,gname,i.Name))
if i.Name != "NSAutoreleasePool" { if i.Name != "NSAutoreleasePool" {
w.cCode.WriteString(fmt.Sprintf(` w.cCode.WriteString(fmt.Sprintf(`
@ -1183,6 +1197,8 @@ void*
of.WriteString(w.cgoFlags.String() + "\n") of.WriteString(w.cgoFlags.String() + "\n")
ef.WriteString(w.cgoFlags.String() + "\n") ef.WriteString(w.cgoFlags.String() + "\n")
of.WriteString(w.cImports.String() + "\n")
ef.WriteString(w.cImports.String() + "\n")
of.WriteString(w.cCode.String()) of.WriteString(w.cCode.String())
of.WriteString(` of.WriteString(`