godot_testability_runtime/
ffi.rs1use std::ffi::{c_char, c_int, c_void};
7
8#[repr(C)]
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum GDExtensionInitializationLevel {
12 Core = 0,
13 Servers = 1,
14 Scene = 2,
15 Editor = 3,
16 MaxInitializationLevel = 4,
17}
18
19pub type GDExtensionBool = u8;
21
22pub type GDExtensionClassLibraryPtr = *mut c_void;
24
25pub type GDExtensionInterfaceGetProcAddress =
27 unsafe extern "C" fn(function_name: *const c_char) -> Option<unsafe extern "C" fn()>;
28
29#[repr(C)]
31#[derive(Debug)]
32pub struct GDExtensionInitialization {
33 pub minimum_initialization_level: GDExtensionInitializationLevel,
35 pub userdata: *mut c_void,
37 pub initialize:
39 Option<extern "C" fn(userdata: *mut c_void, level: GDExtensionInitializationLevel)>,
40 pub deinitialize:
42 Option<extern "C" fn(userdata: *mut c_void, level: GDExtensionInitializationLevel)>,
43}
44
45pub type GDExtensionInitializationFunction = extern "C" fn(
47 get_proc_address: GDExtensionInterfaceGetProcAddress,
48 library: GDExtensionClassLibraryPtr,
49 initialization: *mut GDExtensionInitialization,
50) -> GDExtensionBool;
51
52#[repr(C)]
54#[derive(Debug)]
55pub struct GDExtensionGodotVersion {
56 pub major: u32,
57 pub minor: u32,
58 pub patch: u32,
59 pub string: *const c_char,
60}
61
62pub type LibgodotGDExtensionBindCallback = extern "C" fn(
64 get_proc_address: Option<GDExtensionInterfaceGetProcAddress>,
65 library: GDExtensionClassLibraryPtr,
66 initialization: *mut GDExtensionInitialization,
67) -> c_int;
68
69pub type LibgodotSceneCallback = extern "C" fn(scene_tree: *mut c_void);
71
72extern "C" {
74 pub fn libgodot_init_resource();
76
77 pub fn libgodot_scene_load(scene: *mut c_void);
79
80 pub fn libgodot_is_scene_loadable() -> c_int;
82
83 pub fn libgodot_gdextension_bind(
85 initialization_bind: LibgodotGDExtensionBindCallback,
86 scene_function_bind: Option<LibgodotSceneCallback>,
87 );
88
89 pub fn godot_main(argc: c_int, argv: *const *const c_char) -> c_int;
91}
92
93#[derive(Debug)]
95pub struct LibgodotRuntime {
96 initialized: bool,
97 }
100
101impl LibgodotRuntime {
102 pub fn new() -> Self {
104 Self { initialized: false }
105 }
106
107 pub fn initialize(&mut self) -> Result<(), String> {
109 if self.initialized {
110 return Err("Runtime already initialized".to_string());
111 }
112
113 self.initialized = true;
114 Ok(())
115 }
116
117 pub fn run_main(&mut self, args: &[String]) -> Result<i32, String> {
119 if !self.initialized {
120 return Err("Runtime not initialized".to_string());
121 }
122
123 let c_strings: Vec<std::ffi::CString> = args
125 .iter()
126 .map(|s| std::ffi::CString::new(s.as_str()).unwrap())
127 .collect();
128
129 let c_args: Vec<*const c_char> = c_strings.iter().map(|s| s.as_ptr()).collect();
130
131 let result = unsafe { godot_main(c_args.len() as c_int, c_args.as_ptr()) };
132
133 Ok(result)
134 }
135
136 pub fn is_initialized(&self) -> bool {
138 self.initialized
139 }
140
141 pub fn has_scene_tree(&self) -> bool {
143 self.initialized
145 }
146}
147
148impl Default for LibgodotRuntime {
149 fn default() -> Self {
150 Self::new()
151 }
152}
153
154#[allow(dead_code)]
156extern "C" fn gdextension_init_wrapper(
157 _get_proc_addr: Option<GDExtensionInterfaceGetProcAddress>,
158 library: GDExtensionClassLibraryPtr,
159 init: *mut GDExtensionInitialization,
160) -> c_int {
161 println!("GDExtension initialization callback called");
162
163 if let Some(init_ptr) = unsafe { init.as_mut() } {
164 init_ptr.minimum_initialization_level = GDExtensionInitializationLevel::Core;
165 init_ptr.userdata = library;
166 init_ptr.initialize = Some(default_initialize);
167 init_ptr.deinitialize = Some(default_deinitialize);
168 }
169
170 1 }
172
173#[allow(dead_code)]
174extern "C" fn scene_load_wrapper(scene_tree: *mut c_void) {
175 println!("Scene tree loaded: {:?}", scene_tree);
176}
177
178extern "C" fn default_initialize(_userdata: *mut c_void, level: GDExtensionInitializationLevel) {
180 println!("Initializing GDExtension level: {:?}", level);
181}
182
183extern "C" fn default_deinitialize(_userdata: *mut c_void, level: GDExtensionInitializationLevel) {
184 println!("Deinitializing GDExtension level: {:?}", level);
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn test_runtime_creation() {
193 let runtime = LibgodotRuntime::new();
194 assert!(!runtime.is_initialized());
195 assert!(!runtime.has_scene_tree());
196 }
197
198 #[test]
199 fn test_gdextension_initialization_level() {
200 assert_eq!(GDExtensionInitializationLevel::Core as i32, 0);
201 assert_eq!(GDExtensionInitializationLevel::Scene as i32, 2);
202 }
203}