// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2022 The Ebitengine Authors package objc_test import ( "fmt" "log" "reflect" "structs" "testing" "github.com/bnema/purego" "github.com/bnema/purego/objc" ) func ExampleRegisterClass_helloworld() { class, err := objc.RegisterClass( "FooObject", objc.GetClass("NSObject"), nil, nil, []objc.MethodDef{ { Cmd: objc.RegisterName("run"), Fn: func(self objc.ID, _cmd objc.SEL) { fmt.Println("Hello World!") }, }, }, ) if err != nil { panic(err) } object := objc.ID(class).Send(objc.RegisterName("new")) object.Send(objc.RegisterName("run")) // Output: Hello World! } func ExampleRegisterClass() { var class objc.Class var ( sel_new = objc.RegisterName("new") sel_init = objc.RegisterName("init") sel_setBar = objc.RegisterName("setBar:") sel_bar = objc.RegisterName("bar") BarInit = func(id objc.ID, cmd objc.SEL) objc.ID { return id.SendSuper2(class, cmd) } ) var err error class, err = objc.RegisterClass( "BarObject", objc.GetClass("NSObject"), []*objc.Protocol{ objc.GetProtocol("NSDelegateWindow"), }, []objc.FieldDef{ { Name: "bar", Type: reflect.TypeFor[int](), Attribute: objc.ReadWrite, }, { Name: "foo", Type: reflect.TypeFor[bool](), Attribute: objc.ReadWrite, }, }, []objc.MethodDef{ { Cmd: sel_init, Fn: BarInit, }, }, ) if err != nil { panic(err) } object := objc.ID(class).Send(sel_new) object.Send(sel_setBar, 123) bar := int(object.Send(sel_bar)) fmt.Println(bar) // Output: 123 } func ExampleIMP() { imp := objc.NewIMP(func(self objc.ID, _cmd objc.SEL, a3, a4, a5, a6, a7, a8, a9 int) { fmt.Println("IMP:", self, _cmd, a3, a4, a5, a6, a7, a8, a9) }) purego.SyscallN(uintptr(imp), 105, 567, 9, 2, 3, ^uintptr(4), 4, 8, 9) // Output: IMP: 105 567 9 2 3 -5 4 8 9 } // This example shows three-level inheritance with Base <- Child1 <- Child2. // SendSuper2 takes Child1, the class defining the method. func ExampleID_SendSuper2() { _, err := purego.Dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", purego.RTLD_GLOBAL|purego.RTLD_NOW) if err != nil { panic(err) } sel := objc.RegisterName("run") base, err := objc.RegisterClass( "SuperExampleBase", objc.GetClass("NSObject"), nil, nil, []objc.MethodDef{ { Cmd: sel, Fn: func(self objc.ID, cmd objc.SEL) { fmt.Println("Base") }, }, }, ) if err != nil { panic(err) } var child1 objc.Class child1, err = objc.RegisterClass( "SuperExampleChild1", base, nil, nil, []objc.MethodDef{ { Cmd: sel, Fn: func(self objc.ID, cmd objc.SEL) { // Start lookup in the superclass of Child1. fmt.Print("SendSuper2: ") self.SendSuper2(child1, cmd) }, }, }, ) if err != nil { panic(err) } // Child2 inherits Child1's implementation of run. child2, err := objc.RegisterClass("SuperExampleChild2", child1, nil, nil, nil) if err != nil { panic(err) } for _, class := range []objc.Class{child1, child2} { object := objc.ID(class).Send(objc.RegisterName("new")) defer object.Send(objc.RegisterName("release")) if class == child1 { fmt.Println("Child1 instance") } else { fmt.Println("Child2 instance") } object.Send(sel) } // Output: // Child1 instance // SendSuper2: Base // Child2 instance // SendSuper2: Base } func TestSend(t *testing.T) { // NSNumber comes from Foundation so make sure we have linked to that framework. _, err := purego.Dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", purego.RTLD_GLOBAL|purego.RTLD_NOW) if err != nil { t.Fatal(err) } const double = float64(2.34) // Initialize a NSNumber NSNumber := objc.ID(objc.GetClass("NSNumber")).Send(objc.RegisterName("numberWithDouble:"), double) // Then get that number back using the generic Send function. number := objc.Send[float64](NSNumber, objc.RegisterName("doubleValue")) if double != number { t.Failed() } } func ExampleSend() { type NSRange struct { _ structs.HostLayout Location, Range uint } class_NSString := objc.GetClass("NSString") sel_stringWithUTF8String := objc.RegisterName("stringWithUTF8String:") fullString := objc.ID(class_NSString).Send(sel_stringWithUTF8String, "Hello, World!\x00") subString := objc.ID(class_NSString).Send(sel_stringWithUTF8String, "lo, Wor\x00") r := objc.Send[NSRange](fullString, objc.RegisterName("rangeOfString:"), subString) fmt.Println(r.Location, r.Range) // Output: 3 7 } func ExampleSendSuper2() { super, err := objc.RegisterClass( "SuperObject2", objc.GetClass("NSObject"), nil, nil, []objc.MethodDef{ { Cmd: objc.RegisterName("doSomething"), Fn: func(self objc.ID, _cmd objc.SEL) int { return 16 }, }, }, ) if err != nil { panic(err) } child, err := objc.RegisterClass( "ChildObject2", super, nil, nil, []objc.MethodDef{ { Cmd: objc.RegisterName("doSomething"), Fn: func(self objc.ID, _cmd objc.SEL) int { return 24 }, }, }, ) if err != nil { panic(err) } res := objc.SendSuper2[int](objc.ID(child).Send(objc.RegisterName("new")), child, objc.RegisterName("doSomething")) fmt.Println(res) // Output: 16 } func ExampleAllocateProtocol() { var p *objc.Protocol if p = objc.AllocateProtocol("MyCustomProtocol"); p != nil { p.AddMethodDescription(objc.RegisterName("isFoo"), "B16@0:8", true, true) var adoptedProtocol *objc.Protocol adoptedProtocol = objc.GetProtocol("NSObject") if adoptedProtocol == nil { log.Fatalln("protocol 'NSObject' does not exist") } p.AddProtocol(adoptedProtocol) p.AddProperty("accessibilityElement", []objc.PropertyAttribute{ {Name: &[]byte("T\x00")[0], Value: &[]byte("B\x00")[0]}, {Name: &[]byte("G\x00")[0], Value: &[]byte("isBar\x00")[0]}, }, true, true) p.Register() } p = objc.GetProtocol("MyCustomProtocol") for _, protocol := range p.CopyProtocolList() { fmt.Println(protocol.Name()) } for _, property := range p.CopyPropertyList(true, true) { fmt.Println(property.Name(), property.Attributes()) } for _, method := range p.CopyMethodDescriptionList(true, true) { fmt.Println(method.Name(), method.Types()) } // Output: // NSObject // accessibilityElement TB,GisBar // isFoo B16@0:8 }