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