v0.9.0 291e8af

main_darwin.go RawHistoryFolder

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2025 The Ebitengine Authors
3
4package main
5
6// #cgo CFLAGS: -x objective-c
7// #cgo LDFLAGS: -framework AppKit
8//
9// #import <AppKit/AppKit.h>
10//
11// __attribute__((used))
12// static void __force_protocol_load() {
13//	id o = NULL;
14//	o = @protocol(NSAccessibility);
15//	o = @protocol(NSApplicationDelegate);
16// }
17import "C"
18
19import (
20	"fmt"
21	"io"
22	"os"
23	"strconv"
24	"strings"
25	"text/template"
26
27	"github.com/ebitengine/purego/objc"
28)
29
30var protocols = []string{
31	"NSAccessibility",
32	"NSApplicationDelegate",
33}
34
35type ProtocolImpl struct {
36	Name string
37
38	RequiredInstanceMethods []objc.MethodDescription
39	RequiredClassMethods    []objc.MethodDescription
40	OptionalInstanceMethods []objc.MethodDescription
41	OptionalClassMethods    []objc.MethodDescription
42
43	AdoptedProtocols []*objc.Protocol
44
45	RequiredInstanceProperties []objc.Property
46	RequiredClassProperties    []objc.Property
47	OptionalInstanceProperties []objc.Property
48	OptionalClassProperties    []objc.Property
49}
50
51func readProtocols(names []string) (imps []ProtocolImpl, err error) {
52	for _, name := range names {
53		p := objc.GetProtocol(name)
54		if p == nil {
55			return nil, fmt.Errorf("protocol '%s' does not exist", name)
56		}
57		imp := ProtocolImpl{}
58		imp.Name = name
59		imp.RequiredInstanceMethods = p.CopyMethodDescriptionList(true, true)
60		imp.RequiredClassMethods = p.CopyMethodDescriptionList(true, false)
61		imp.OptionalInstanceMethods = p.CopyMethodDescriptionList(false, true)
62		imp.OptionalClassMethods = p.CopyMethodDescriptionList(false, false)
63
64		imp.AdoptedProtocols = p.CopyProtocolList()
65
66		imp.RequiredInstanceProperties = p.CopyPropertyList(true, true)
67		imp.RequiredClassProperties = p.CopyPropertyList(true, false)
68		imp.OptionalInstanceProperties = p.CopyPropertyList(false, true)
69		imp.OptionalClassProperties = p.CopyPropertyList(false, false)
70		imps = append(imps, imp)
71	}
72	return imps, nil
73}
74
75const templ = `// Code generated by protocol-dumper; DO NOT EDIT.
76
77package main
78
79import (
80	"log"
81
82	"github.com/ebitengine/purego/objc"
83)
84
85func init() {
86	var p *objc.Protocol
87	{{- range . }}
88	{{- $protocolName := .Name }}
89	if p = objc.AllocateProtocol("{{$protocolName}}"); p != nil {
90		{{- range .RequiredInstanceMethods }}
91		p.AddMethodDescription(objc.RegisterName("{{ .Name }}"), "{{ .Types }}", true, true)
92		{{- end }}
93		
94		{{- range .RequiredClassMethods }}
95		p.AddMethodDescription(objc.RegisterName("{{ .Name }}"), "{{ .Types }}", true, false)
96		{{- end }}
97		
98		{{- range .OptionalInstanceMethods }}
99		p.AddMethodDescription(objc.RegisterName("{{ .Name }}"), "{{ .Types }}", false, true)
100		{{- end }}
101		
102		{{- range .OptionalClassMethods }}
103		p.AddMethodDescription(objc.RegisterName("{{ .Name }}"), "{{ .Types }}", false, false)
104		{{- end }}
105		var adoptedProtocol *objc.Protocol
106		{{- range .AdoptedProtocols }}
107		adoptedProtocol = objc.GetProtocol("{{ .Name }}")
108		if adoptedProtocol == nil {
109			log.Fatalln("protocol '{{ .Name }}' does not exist")
110		}
111		p.AddProtocol(adoptedProtocol)
112		{{- end }}
113
114		{{- range .RequiredInstanceProperties }}
115		p.AddProperty("{{ .Name }}", {{ attributeToStructString . }}, true, true)
116		{{- end }}
117
118
119		{{- range .RequiredClassProperties }}
120		p.AddProperty("{{ .Name }}", {{ attributeToStructString . }}, true, false)
121		{{- end }}
122
123		{{- range .OptionalInstanceProperties }}
124		p.AddProperty("{{ .Name }}", {{ attributeToStructString . }}, false, true)
125		{{- end }}
126
127		{{- range .OptionalClassProperties }}
128		p.AddProperty("{{ .Name }}", {{ attributeToStructString . }}, false, false)
129		{{- end }}
130		p.Register()
131	} // Finished protocol: {{$protocolName}}
132	{{- end }}
133}
134`
135
136func printProtocols(impls []ProtocolImpl, w io.Writer) error {
137	tmpl, err := template.New("protocol.tmpl").Funcs(template.FuncMap{
138		"attributeToStructString": attributeToStructString,
139	}).Parse(templ)
140	if err != nil {
141		return err
142	}
143	err = tmpl.Execute(w, impls)
144	if err != nil {
145		return err
146	}
147	return nil
148}
149
150func attributeToStructString(p objc.Property) string {
151	attribs := strings.Split(p.Attributes(), ",")
152	var b strings.Builder
153	b.WriteString("[]objc.PropertyAttribute{")
154	for i, attrib := range attribs {
155		b.WriteString(fmt.Sprintf(`{Name: &[]byte("%s\x00")[0], Value: &[]byte(%s)[0]}`, string(attrib[0]), strconv.Quote(attrib[1:]+"\x00")))
156		if i != len(attribs)-1 {
157			b.WriteString(", ")
158		}
159	}
160	b.WriteString("}")
161	return b.String()
162}
163
164// This program prints out the Go code to generate the OBJC protocols list.
165// To use it add the @protocol(name) to the C import section and then add it to the protocols list.
166// You'll want to run it as amd64 and arm64 since the types are slightly different for each architecture.
167func main() {
168	var imps []ProtocolImpl
169	var err error
170	if imps, err = readProtocols(protocols); err != nil {
171		panic(err)
172	}
173	if err = printProtocols(imps, os.Stdout); err != nil {
174		panic(err)
175	}
176}

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