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