1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
3
4package objc_test
5
6import (
7 "fmt"
8 "log"
9 "reflect"
10 "structs"
11 "testing"
12
13 "github.com/bnema/purego"
14 "github.com/bnema/purego/objc"
15)
16
17func ExampleRegisterClass_helloworld() {
18 class, err := objc.RegisterClass(
19 "FooObject",
20 objc.GetClass("NSObject"),
21 nil,
22 nil,
23 []objc.MethodDef{
24 {
25 Cmd: objc.RegisterName("run"),
26 Fn: func(self objc.ID, _cmd objc.SEL) {
27 fmt.Println("Hello World!")
28 },
29 },
30 },
31 )
32 if err != nil {
33 panic(err)
34 }
35
36 object := objc.ID(class).Send(objc.RegisterName("new"))
37 object.Send(objc.RegisterName("run"))
38 // Output: Hello World!
39}
40
41func ExampleRegisterClass() {
42 var class objc.Class
43 var (
44 sel_new = objc.RegisterName("new")
45 sel_init = objc.RegisterName("init")
46 sel_setBar = objc.RegisterName("setBar:")
47 sel_bar = objc.RegisterName("bar")
48
49 BarInit = func(id objc.ID, cmd objc.SEL) objc.ID {
50 return id.SendSuper2(class, cmd)
51 }
52 )
53
54 var err error
55 class, err = objc.RegisterClass(
56 "BarObject",
57 objc.GetClass("NSObject"),
58 []*objc.Protocol{
59 objc.GetProtocol("NSDelegateWindow"),
60 },
61 []objc.FieldDef{
62 {
63 Name: "bar",
64 Type: reflect.TypeFor[int](),
65 Attribute: objc.ReadWrite,
66 },
67 {
68 Name: "foo",
69 Type: reflect.TypeFor[bool](),
70 Attribute: objc.ReadWrite,
71 },
72 },
73 []objc.MethodDef{
74 {
75 Cmd: sel_init,
76 Fn: BarInit,
77 },
78 },
79 )
80 if err != nil {
81 panic(err)
82 }
83
84 object := objc.ID(class).Send(sel_new)
85 object.Send(sel_setBar, 123)
86 bar := int(object.Send(sel_bar))
87 fmt.Println(bar)
88 // Output: 123
89}
90
91func ExampleIMP() {
92 imp := objc.NewIMP(func(self objc.ID, _cmd objc.SEL, a3, a4, a5, a6, a7, a8, a9 int) {
93 fmt.Println("IMP:", self, _cmd, a3, a4, a5, a6, a7, a8, a9)
94 })
95
96 purego.SyscallN(uintptr(imp), 105, 567, 9, 2, 3, ^uintptr(4), 4, 8, 9)
97 // Output: IMP: 105 567 9 2 3 -5 4 8 9
98}
99
100// This example shows three-level inheritance with Base <- Child1 <- Child2.
101// SendSuper2 takes Child1, the class defining the method.
102func ExampleID_SendSuper2() {
103 _, err := purego.Dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", purego.RTLD_GLOBAL|purego.RTLD_NOW)
104 if err != nil {
105 panic(err)
106 }
107
108 sel := objc.RegisterName("run")
109 base, err := objc.RegisterClass(
110 "SuperExampleBase",
111 objc.GetClass("NSObject"),
112 nil,
113 nil,
114 []objc.MethodDef{
115 {
116 Cmd: sel,
117 Fn: func(self objc.ID, cmd objc.SEL) {
118 fmt.Println("Base")
119 },
120 },
121 },
122 )
123 if err != nil {
124 panic(err)
125 }
126
127 var child1 objc.Class
128 child1, err = objc.RegisterClass(
129 "SuperExampleChild1",
130 base,
131 nil,
132 nil,
133 []objc.MethodDef{
134 {
135 Cmd: sel,
136 Fn: func(self objc.ID, cmd objc.SEL) {
137 // Start lookup in the superclass of Child1.
138 fmt.Print("SendSuper2: ")
139 self.SendSuper2(child1, cmd)
140 },
141 },
142 },
143 )
144 if err != nil {
145 panic(err)
146 }
147
148 // Child2 inherits Child1's implementation of run.
149 child2, err := objc.RegisterClass("SuperExampleChild2", child1, nil, nil, nil)
150 if err != nil {
151 panic(err)
152 }
153
154 for _, class := range []objc.Class{child1, child2} {
155 object := objc.ID(class).Send(objc.RegisterName("new"))
156 defer object.Send(objc.RegisterName("release"))
157 if class == child1 {
158 fmt.Println("Child1 instance")
159 } else {
160 fmt.Println("Child2 instance")
161 }
162 object.Send(sel)
163 }
164 // Output:
165 // Child1 instance
166 // SendSuper2: Base
167 // Child2 instance
168 // SendSuper2: Base
169}
170
171func TestSend(t *testing.T) {
172 // NSNumber comes from Foundation so make sure we have linked to that framework.
173 _, err := purego.Dlopen("/System/Library/Frameworks/Foundation.framework/Foundation", purego.RTLD_GLOBAL|purego.RTLD_NOW)
174 if err != nil {
175 t.Fatal(err)
176 }
177 const double = float64(2.34)
178 // Initialize a NSNumber
179 NSNumber := objc.ID(objc.GetClass("NSNumber")).Send(objc.RegisterName("numberWithDouble:"), double)
180 // Then get that number back using the generic Send function.
181 number := objc.Send[float64](NSNumber, objc.RegisterName("doubleValue"))
182 if double != number {
183 t.Failed()
184 }
185}
186
187func ExampleSend() {
188 type NSRange struct {
189 _ structs.HostLayout
190 Location, Range uint
191 }
192 class_NSString := objc.GetClass("NSString")
193 sel_stringWithUTF8String := objc.RegisterName("stringWithUTF8String:")
194
195 fullString := objc.ID(class_NSString).Send(sel_stringWithUTF8String, "Hello, World!\x00")
196 subString := objc.ID(class_NSString).Send(sel_stringWithUTF8String, "lo, Wor\x00")
197
198 r := objc.Send[NSRange](fullString, objc.RegisterName("rangeOfString:"), subString)
199 fmt.Println(r.Location, r.Range)
200 // Output: 3 7
201}
202
203func ExampleSendSuper2() {
204 super, err := objc.RegisterClass(
205 "SuperObject2",
206 objc.GetClass("NSObject"),
207 nil,
208 nil,
209 []objc.MethodDef{
210 {
211 Cmd: objc.RegisterName("doSomething"),
212 Fn: func(self objc.ID, _cmd objc.SEL) int {
213 return 16
214 },
215 },
216 },
217 )
218 if err != nil {
219 panic(err)
220 }
221
222 child, err := objc.RegisterClass(
223 "ChildObject2",
224 super,
225 nil,
226 nil,
227 []objc.MethodDef{
228 {
229 Cmd: objc.RegisterName("doSomething"),
230 Fn: func(self objc.ID, _cmd objc.SEL) int {
231 return 24
232 },
233 },
234 },
235 )
236 if err != nil {
237 panic(err)
238 }
239
240 res := objc.SendSuper2[int](objc.ID(child).Send(objc.RegisterName("new")), child, objc.RegisterName("doSomething"))
241 fmt.Println(res)
242 // Output: 16
243}
244
245func ExampleAllocateProtocol() {
246 var p *objc.Protocol
247 if p = objc.AllocateProtocol("MyCustomProtocol"); p != nil {
248 p.AddMethodDescription(objc.RegisterName("isFoo"), "B16@0:8", true, true)
249 var adoptedProtocol *objc.Protocol
250 adoptedProtocol = objc.GetProtocol("NSObject")
251 if adoptedProtocol == nil {
252 log.Fatalln("protocol 'NSObject' does not exist")
253 }
254 p.AddProtocol(adoptedProtocol)
255 p.AddProperty("accessibilityElement", []objc.PropertyAttribute{
256 {Name: &[]byte("T\x00")[0], Value: &[]byte("B\x00")[0]},
257 {Name: &[]byte("G\x00")[0], Value: &[]byte("isBar\x00")[0]},
258 }, true, true)
259 p.Register()
260 }
261
262 p = objc.GetProtocol("MyCustomProtocol")
263
264 for _, protocol := range p.CopyProtocolList() {
265 fmt.Println(protocol.Name())
266 }
267 for _, property := range p.CopyPropertyList(true, true) {
268 fmt.Println(property.Name(), property.Attributes())
269 }
270 for _, method := range p.CopyMethodDescriptionList(true, true) {
271 fmt.Println(method.Name(), method.Types())
272 }
273
274 // Output:
275 // NSObject
276 // accessibilityElement TB,GisBar
277 // isFoo B16@0:8
278}