v0.11.0-alpha.11 63002e0

objc_runtime_darwin_test.go RawHistoryFolder

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}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close