This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch main
in repository ego.

View the commit online.

commit 56b01c4cb07f0093d1b2c87d0b07e107454c22d9
Author: [email protected] <[email protected]>
AuthorDate: Mon Mar 30 16:40:27 2026 -0600

    feat(ego-gen): add global function support with package-level generation
    
    Add GlobalFunctionData struct, globalFunctionSet for 24 receiver-less
    EFL functions, and template section generating package-level Go
    functions instead of methods.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 cmd/ego-gen/generator.go            |  12 ++++
 cmd/ego-gen/generator_test.go       | 113 ++++++++++++++++++++++++++++++++++++
 cmd/ego-gen/global_function.go      |  68 ++++++++++++++++++++++
 cmd/ego-gen/templates/class.go.tmpl |  29 +++++++++
 4 files changed, 222 insertions(+)

diff --git a/cmd/ego-gen/generator.go b/cmd/ego-gen/generator.go
index ad25351..7de63ff 100644
--- a/cmd/ego-gen/generator.go
+++ b/cmd/ego-gen/generator.go
@@ -36,6 +36,7 @@ type ClassData struct {
 	IteratorMethods      []IteratorMethodData  // methods returning Eina_Iterator
 	HasIterators         bool                  // true if IteratorMethods is non-empty (controls import)
 	IteratorStructTypes  []IteratorMethodData  // one entry per distinct struct elem type (for C helpers)
+	GlobalFunctions []GlobalFunctionData // package-level functions with no Eo receiver
 }
 
 // ConstructorData describes a constructor option for an EFL class. Each entry
@@ -146,6 +147,17 @@ type IndexedPropertyData struct {
 	MultiSetValues []MultiValueData
 }
 
+// GlobalFunctionData describes an EFL global (class-level) function with no
+// Eo* receiver. These generate package-level Go functions instead of methods.
+type GlobalFunctionData struct {
+	GoName      string      // e.g. "EventGlobalFreezeCount"
+	CName       string      // e.g. "efl_event_global_freeze_count_get"
+	Params      []ParamData // input parameters (may be empty)
+	ReturnType  string      // Go return type (empty if void)
+	ReturnCType string      // C return type
+	IsEoReturn  bool        // true if return type is Eo*
+}
+
 // IteratorMethodData describes an EFL method that returns an Eina_Iterator.
 // The generated Go method returns iter.Seq[T] wrapping the C iterator lifecycle.
 type IteratorMethodData struct {
diff --git a/cmd/ego-gen/generator_test.go b/cmd/ego-gen/generator_test.go
index 5fc642e..ca46415 100644
--- a/cmd/ego-gen/generator_test.go
+++ b/cmd/ego-gen/generator_test.go
@@ -906,3 +906,116 @@ func TestGenerateClass_IteratorMethod(t *testing.T) {
 		}
 	}
 }
+
+func TestGenerateClass_GlobalFunction(t *testing.T) {
+	g, outDir := newTestGenerator(t)
+
+	data := ClassData{
+		PackageName:   "eo",
+		GoName:        "Object",
+		EolianName:    "Efl.Object",
+		CClassName:    "EFL_OBJECT_CLASS",
+		CClassGetFunc: "efl_object_class_get",
+		CPrefix:       "efl_object",
+		EOHeaderFile:  "efl_object.eo.h",
+		IsAbstract:    true,
+		GlobalFunctions: []GlobalFunctionData{
+			{
+				GoName:      "EventGlobalFreezeCount",
+				CName:       "efl_event_global_freeze_count_get",
+				ReturnType:  "uint",
+				ReturnCType: "unsigned int",
+			},
+			{
+				GoName: "EventGlobalThaw",
+				CName:  "efl_event_global_thaw",
+			},
+		},
+	}
+
+	if err := g.GenerateClass(data); err != nil {
+		t.Fatalf("GenerateClass: %v", err)
+	}
+
+	outFile := filepath.Join(outDir, "eo", "object.go")
+	content, err := os.ReadFile(outFile)
+	if err != nil {
+		t.Fatalf("output file not found at %s: %v", outFile, err)
+	}
+
+	src := string(content)
+
+	mustContain := []string{
+		"func EventGlobalFreezeCount() uint",
+		"efl_event_global_freeze_count_get",
+		"func EventGlobalThaw()",
+		"efl_event_global_thaw",
+	}
+
+	mustNotContain := []string{
+		"(o *Object) EventGlobalFreezeCount",
+		"(o *Object) EventGlobalThaw",
+	}
+
+	for _, want := range mustContain {
+		if !strings.Contains(src, want) {
+			t.Errorf("output missing expected pattern %q\nfile content:\n%s", want, src)
+		}
+	}
+	for _, banned := range mustNotContain {
+		if strings.Contains(src, banned) {
+			t.Errorf("output contains banned pattern %q", banned)
+		}
+	}
+}
+
+func TestGenerateClass_GlobalFunctionString(t *testing.T) {
+	g, outDir := newTestGenerator(t)
+
+	data := ClassData{
+		PackageName:   "eo",
+		GoName:        "TextMarkupUtil",
+		EolianName:    "Efl.Text_Markup_Util",
+		CClassName:    "EFL_TEXT_MARKUP_UTIL_CLASS",
+		CClassGetFunc: "efl_text_markup_util_class_get",
+		CPrefix:       "efl_text_markup_util",
+		EOHeaderFile:  "efl_text_markup_util.eo.h",
+		IsAbstract:    true,
+		GlobalFunctions: []GlobalFunctionData{
+			{
+				GoName:      "TextToMarkup",
+				CName:       "efl_text_markup_util_text_to_markup",
+				Params: []ParamData{
+					{GoName: "text", GoType: "string", CType: "const char *"},
+				},
+				ReturnType:  "string",
+				ReturnCType: "const char *",
+			},
+		},
+	}
+
+	if err := g.GenerateClass(data); err != nil {
+		t.Fatalf("GenerateClass: %v", err)
+	}
+
+	outFile := filepath.Join(outDir, "eo", "text_markup_util.go")
+	content, err := os.ReadFile(outFile)
+	if err != nil {
+		t.Fatalf("output file not found at %s: %v", outFile, err)
+	}
+
+	src := string(content)
+
+	mustContain := []string{
+		"func TextToMarkup(text string) string",
+		"C.CString(text)",
+		"C.GoString(",
+		"efl_text_markup_util_text_to_markup",
+	}
+
+	for _, want := range mustContain {
+		if !strings.Contains(src, want) {
+			t.Errorf("output missing expected pattern %q\nfile content:\n%s", want, src)
+		}
+	}
+}
diff --git a/cmd/ego-gen/global_function.go b/cmd/ego-gen/global_function.go
new file mode 100644
index 0000000..75ec863
--- /dev/null
+++ b/cmd/ego-gen/global_function.go
@@ -0,0 +1,68 @@
+package main
+
+// globalFunctionSet contains C function names that are global (no Eo* first
+// parameter). These are routed through the global function codegen path
+// instead of being skipped.
+var globalFunctionSet = map[string]bool{
+	"efl_event_global_thaw":                           true,
+	"efl_event_global_freeze":                         true,
+	"efl_event_global_freeze_count_get":               true,
+	"efl_text_markup_util_text_to_markup":             true,
+	"efl_text_markup_util_markup_to_text":             true,
+	"efl_env_self":                                    true,
+	"efl_animation_default_duration_set":              true,
+	"efl_app_main_get":                                true,
+	"efl_net_ssl_context_default_dialer_get":          true,
+	"efl_net_dialer_http_date_parse":                  true,
+	"efl_net_dialer_http_date_serialize":              true,
+	"efl_net_ip_address_sockaddr_set":                 true,
+	"efl_net_ip_address_create":                       true,
+	"efl_net_ip_address_create_sockaddr":              true,
+	"efl_net_ip_address_parse":                        true,
+	"efl_net_ip_address_resolve":                      true,
+	"efl_ui_action_connector_bind_clickable_to_theme": true,
+	"efl_ui_action_connector_bind_clickable_to_object": true,
+	"efl_ui_focus_util_active_manager":                true,
+	"efl_ui_spotlight_util_stack_gen":                 true,
+	"efl_ui_view_factory_create_with_event":           true,
+	"efl_ui_win_exit_on_all_windows_closed_set":       true,
+	"efl_ui_theme_default_get":                        true,
+	"efl_access_object_access_root_get":               true,
+}
+
+// buildGlobalFunctionData converts an Eolian method Function that has no Eo*
+// receiver into a GlobalFunctionData for package-level code generation.
+func buildGlobalFunctionData(f *Function) GlobalFunctionData {
+	methodName := f.Name()
+	goName := SnakeToCamel(methodName)
+	cName := f.FullCName(FunctionMethod)
+
+	var params []ParamData
+	for _, p := range f.Parameters() {
+		if p.Direction() != ParamIn {
+			continue
+		}
+		params = append(params, buildParamData(p))
+	}
+
+	var returnType, returnCType string
+	var isEoReturn bool
+	if rt := f.ReturnType(FunctionMethod); rt != nil {
+		returnCType = rt.CType()
+		returnType = CTypeToGo(returnCType)
+		isEoReturn = rt.IsClass()
+		if returnCType == "void" || returnType == "" {
+			returnType = ""
+			returnCType = ""
+		}
+	}
+
+	return GlobalFunctionData{
+		GoName:      goName,
+		CName:       cName,
+		Params:      params,
+		ReturnType:  returnType,
+		ReturnCType: returnCType,
+		IsEoReturn:  isEoReturn,
+	}
+}
diff --git a/cmd/ego-gen/templates/class.go.tmpl b/cmd/ego-gen/templates/class.go.tmpl
index 559bb32..f40cfa9 100644
--- a/cmd/ego-gen/templates/class.go.tmpl
+++ b/cmd/ego-gen/templates/class.go.tmpl
@@ -591,6 +591,35 @@ func (o *{{$.GoName}}) {{$im.GoName}}({{range $i, $p := $im.Params}}{{if $i}}, {
 }
 {{- end}}
 
+{{- range $gf := .GlobalFunctions}}
+
+// {{$gf.GoName}} is a package-level EFL function with no object receiver.
+func {{$gf.GoName}}({{range $i, $p := $gf.Params}}{{if $i}}, {{end}}{{$p.GoName}} {{if $p.IsEo}}efl.Objecter{{else}}{{$p.GoType}}{{end}}{{end}}){{if $gf.ReturnType}} {{$gf.ReturnType}}{{end}} {
+{{- range $gf.Params}}
+{{- if needsStringConversion .CType}}
+    c{{.GoName}} := C.CString({{.GoName}})
+    defer C.free(unsafe.Pointer(c{{.GoName}}))
+{{- else if isBoolType .CType}}
+    var c{{.GoName}} C.{{cTypeToCgo .CType}}
+    if {{.GoName}} { c{{.GoName}} = 1 }
+{{- end}}
+{{- end}}
+{{- if $gf.ReturnType}}
+{{- if needsStringConversion $gf.ReturnCType}}
+    return C.GoString(C.{{$gf.CName}}({{range $i, $p := $gf.Params}}{{if $i}}, {{end}}{{template "paramArg" $p}}{{end}}))
+{{- else if isBoolType $gf.ReturnCType}}
+    return C.{{$gf.CName}}({{range $i, $p := $gf.Params}}{{if $i}}, {{end}}{{template "paramArg" $p}}{{end}}) != 0
+{{- else if $gf.IsEoReturn}}
+    return unsafe.Pointer(C.{{$gf.CName}}({{range $i, $p := $gf.Params}}{{if $i}}, {{end}}{{template "paramArg" $p}}{{end}}))
+{{- else}}
+    return {{$gf.ReturnType}}(C.{{$gf.CName}}({{range $i, $p := $gf.Params}}{{if $i}}, {{end}}{{template "paramArg" $p}}{{end}}))
+{{- end}}
+{{- else}}
+    C.{{$gf.CName}}({{range $i, $p := $gf.Params}}{{if $i}}, {{end}}{{template "paramArg" $p}}{{end}})
+{{- end}}
+}
+{{- end}}
+
 {{- define "paramArg"}}
 {{- if .IsEo}}(*C.Eo)({{.GoName}}.Eo())
 {{- else if or (needsStringConversion .CType) (isBoolType .CType)}}c{{.GoName}}

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to