Skip to main content

rustapi/
lib.rs

1#[cfg(not(target_arch = "wasm32"))]
2#[macro_use]
3mod macros;
4
5pub mod actionlist;
6pub mod actions;
7pub mod configeditor;
8pub mod dockmanager;
9pub mod docks;
10pub mod editcamera;
11pub mod editor;
12pub mod editor_tools;
13pub mod hud;
14#[cfg(not(target_arch = "wasm32"))]
15pub mod i18n;
16pub mod mapeditor;
17pub mod minimap;
18pub mod misc;
19#[cfg(all(not(target_arch = "wasm32"), feature = "self-update"))]
20pub mod self_update;
21pub mod sidebar;
22pub mod toollist;
23pub mod tools;
24pub mod undo;
25pub mod utils;
26
27use rust_embed::RustEmbed;
28#[derive(RustEmbed)]
29#[folder = "embedded/"]
30#[exclude = "*.DS_Store"]
31pub struct Embedded;
32
33pub const DEFAULT_VLAYOUT_RATIO: f32 = 0.62;
34
35#[allow(ambiguous_glob_reexports)]
36pub mod prelude {
37    pub use ::serde::{Deserialize, Serialize};
38
39    pub use codegridfx::prelude::*;
40    pub use shared::prelude::*;
41    pub use std::sync::{LazyLock, RwLock};
42    pub use theframework::prelude::*;
43
44    pub use crate::mapeditor::*;
45    pub use crate::misc::*;
46    // pub use crate::previewview::*;
47    pub use crate::actionlist::*;
48    pub use crate::sidebar::*;
49    pub use crate::toollist::*;
50    pub use crate::undo::project_atoms::*;
51    pub use crate::undo::project_helper::*;
52    pub use crate::undo::project_undo::*;
53    pub use crate::undo::*;
54    pub use crate::utils::*;
55
56    pub use crate::tools::game::GameTool;
57    pub use crate::tools::linedef::LinedefTool;
58    pub use crate::tools::sector::SectorTool;
59    pub use crate::tools::selection::SelectionTool;
60    // pub use crate::tools::tileset::TilesetTool;
61    pub use crate::tools::vertex::VertexTool;
62
63    pub use crate::docks::tiles::*;
64
65    pub use crate::actions::*;
66    pub use crate::docks::*;
67    pub use crate::editor_tools::*;
68    pub use crate::tools::*;
69
70    pub use crate::configeditor::ConfigEditor;
71    pub use crate::editcamera::{CustomMoveAction, EditCamera};
72
73    pub use crate::dockmanager::{DockManager, DockManagerState};
74
75    pub use toml::Table;
76
77    pub const KEY_ESCAPE: u32 = 0;
78    pub const KEY_RETURN: u32 = 1;
79    pub const KEY_DELETE: u32 = 2;
80    pub const KEY_UP: u32 = 3;
81    pub const KEY_RIGHT: u32 = 4;
82    pub const KEY_DOWN: u32 = 5;
83    pub const KEY_LEFT: u32 = 6;
84    pub const KEY_SPACE: u32 = 7;
85    pub const KEY_TAB: u32 = 8;
86}
87
88// --- FFI exports for the Xcode static library build ---
89
90#[cfg(feature = "staticlib")]
91mod ffi {
92    use super::editor::Editor;
93    use super::prelude::*;
94
95    use std::ffi::{CStr, CString};
96    use std::os::raw::c_char;
97    use std::ptr;
98
99    use lazy_static::lazy_static;
100    use std::sync::Mutex;
101
102    lazy_static! {
103        static ref APP: Mutex<Editor> = Mutex::new(Editor::new());
104        static ref CTX: Mutex<TheContext> = Mutex::new(TheContext::new(800, 600, 1.0));
105        static ref UI: Mutex<TheUI> = Mutex::new(TheUI::new());
106    }
107
108    #[unsafe(no_mangle)]
109    pub extern "C" fn rust_init() {
110        UI.lock().unwrap().init(&mut CTX.lock().unwrap());
111        APP.lock().unwrap().init(&mut CTX.lock().unwrap());
112        APP.lock()
113            .unwrap()
114            .init_ui(&mut UI.lock().unwrap(), &mut CTX.lock().unwrap());
115
116        // Keep startup behavior aligned with winit path.
117        APP.lock().unwrap().set_cmd_line_args(
118            vec!["eldiron-creator".to_string()],
119            &mut CTX.lock().unwrap(),
120        );
121    }
122
123    /// # Safety
124    #[unsafe(no_mangle)]
125    pub unsafe extern "C" fn rust_draw(pixels: *mut u8, width: u32, height: u32) {
126        let length = width as usize * height as usize * 4;
127        let slice = unsafe { std::slice::from_raw_parts_mut(pixels, length) };
128
129        CTX.lock().unwrap().width = width as usize;
130        CTX.lock().unwrap().height = height as usize;
131
132        UI.lock().unwrap().draw(slice, &mut CTX.lock().unwrap());
133    }
134
135    #[unsafe(no_mangle)]
136    pub extern "C" fn rust_update() -> bool {
137        UI.lock().unwrap().update(&mut CTX.lock().unwrap());
138        APP.lock()
139            .unwrap()
140            .update_ui(&mut UI.lock().unwrap(), &mut CTX.lock().unwrap());
141        APP.lock().unwrap().update(&mut CTX.lock().unwrap())
142    }
143
144    #[unsafe(no_mangle)]
145    pub extern "C" fn rust_target_fps() -> u32 {
146        APP.lock().unwrap().target_fps().clamp(1.0, 120.0) as u32
147    }
148
149    #[unsafe(no_mangle)]
150    pub extern "C" fn rust_hover(x: f32, y: f32) -> bool {
151        UI.lock().unwrap().hover(x, y, &mut CTX.lock().unwrap())
152    }
153
154    #[unsafe(no_mangle)]
155    pub extern "C" fn rust_touch_down(x: f32, y: f32) -> bool {
156        UI.lock()
157            .unwrap()
158            .touch_down(x, y, &mut CTX.lock().unwrap())
159    }
160
161    #[unsafe(no_mangle)]
162    pub extern "C" fn rust_touch_dragged(x: f32, y: f32) -> bool {
163        UI.lock()
164            .unwrap()
165            .touch_dragged(x, y, &mut CTX.lock().unwrap())
166    }
167
168    #[unsafe(no_mangle)]
169    pub extern "C" fn rust_touch_up(x: f32, y: f32) -> bool {
170        UI.lock().unwrap().touch_up(x, y, &mut CTX.lock().unwrap())
171    }
172
173    #[unsafe(no_mangle)]
174    pub extern "C" fn rust_touch_wheel(x: f32, y: f32) -> bool {
175        UI.lock()
176            .unwrap()
177            .mouse_wheel((x as i32, y as i32), &mut CTX.lock().unwrap())
178    }
179
180    /// # Safety
181    #[unsafe(no_mangle)]
182    pub unsafe extern "C" fn rust_key_down(p: *const c_char) -> bool {
183        let c_str = unsafe { CStr::from_ptr(p) };
184        if let Ok(key) = c_str.to_str() {
185            if let Some(ch) = key.chars().next() {
186                return UI
187                    .lock()
188                    .unwrap()
189                    .key_down(Some(ch), None, &mut CTX.lock().unwrap());
190            }
191        }
192        false
193    }
194
195    /// # Safety
196    #[unsafe(no_mangle)]
197    pub unsafe extern "C" fn rust_key_up(p: *const c_char) -> bool {
198        let c_str = unsafe { CStr::from_ptr(p) };
199        if let Ok(key) = c_str.to_str() {
200            if let Some(ch) = key.chars().next() {
201                return UI
202                    .lock()
203                    .unwrap()
204                    .key_up(Some(ch), None, &mut CTX.lock().unwrap());
205            }
206        }
207        false
208    }
209
210    #[unsafe(no_mangle)]
211    pub extern "C" fn rust_special_key_down(key: u32) -> bool {
212        if key == KEY_ESCAPE {
213            UI.lock()
214                .unwrap()
215                .key_down(None, Some(TheKeyCode::Escape), &mut CTX.lock().unwrap())
216        } else if key == KEY_RETURN {
217            UI.lock()
218                .unwrap()
219                .key_down(None, Some(TheKeyCode::Return), &mut CTX.lock().unwrap())
220        } else if key == KEY_DELETE {
221            UI.lock()
222                .unwrap()
223                .key_down(None, Some(TheKeyCode::Delete), &mut CTX.lock().unwrap())
224        } else if key == KEY_UP {
225            UI.lock()
226                .unwrap()
227                .key_down(None, Some(TheKeyCode::Up), &mut CTX.lock().unwrap())
228        } else if key == KEY_RIGHT {
229            UI.lock()
230                .unwrap()
231                .key_down(None, Some(TheKeyCode::Right), &mut CTX.lock().unwrap())
232        } else if key == KEY_DOWN {
233            UI.lock()
234                .unwrap()
235                .key_down(None, Some(TheKeyCode::Down), &mut CTX.lock().unwrap())
236        } else if key == KEY_LEFT {
237            UI.lock()
238                .unwrap()
239                .key_down(None, Some(TheKeyCode::Left), &mut CTX.lock().unwrap())
240        } else if key == KEY_SPACE {
241            UI.lock()
242                .unwrap()
243                .key_down(None, Some(TheKeyCode::Space), &mut CTX.lock().unwrap())
244        } else {
245            UI.lock()
246                .unwrap()
247                .key_down(None, Some(TheKeyCode::Tab), &mut CTX.lock().unwrap())
248        }
249    }
250
251    #[unsafe(no_mangle)]
252    pub extern "C" fn rust_key_modifier_changed(
253        shift: bool,
254        ctrl: bool,
255        alt: bool,
256        logo: bool,
257    ) -> bool {
258        UI.lock()
259            .unwrap()
260            .modifier_changed(shift, ctrl, alt, logo, &mut CTX.lock().unwrap());
261        APP.lock().unwrap().modifier_changed(shift, ctrl, alt, logo)
262    }
263
264    /// # Safety
265    #[unsafe(no_mangle)]
266    pub unsafe extern "C" fn rust_dropped_file(p: *const c_char) {
267        let path_str = unsafe { CStr::from_ptr(p) };
268        if let Ok(path) = path_str.to_str() {
269            APP.lock().unwrap().dropped_file(path.to_string());
270        }
271    }
272
273    #[unsafe(no_mangle)]
274    pub extern "C" fn rust_new() {
275        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
276            TheId::named("New"),
277            TheWidgetState::Clicked,
278        ));
279    }
280
281    #[unsafe(no_mangle)]
282    pub extern "C" fn rust_play() {
283        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
284            TheId::named("Play"),
285            TheWidgetState::Clicked,
286        ));
287    }
288
289    #[unsafe(no_mangle)]
290    pub extern "C" fn rust_pause() {
291        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
292            TheId::named("Pause"),
293            TheWidgetState::Clicked,
294        ));
295    }
296
297    #[unsafe(no_mangle)]
298    pub extern "C" fn rust_stop() {
299        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
300            TheId::named("Stop"),
301            TheWidgetState::Clicked,
302        ));
303    }
304
305    #[unsafe(no_mangle)]
306    pub extern "C" fn rust_open() {
307        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
308            TheId::named("Open"),
309            TheWidgetState::Clicked,
310        ));
311    }
312
313    #[unsafe(no_mangle)]
314    pub extern "C" fn rust_close() {
315        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
316            TheId::named("Close"),
317            TheWidgetState::Clicked,
318        ));
319    }
320
321    #[unsafe(no_mangle)]
322    pub extern "C" fn rust_save() {
323        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
324            TheId::named("Save"),
325            TheWidgetState::Clicked,
326        ));
327    }
328
329    #[unsafe(no_mangle)]
330    pub extern "C" fn rust_save_as() {
331        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
332            TheId::named("Save As"),
333            TheWidgetState::Clicked,
334        ));
335    }
336
337    #[unsafe(no_mangle)]
338    pub extern "C" fn rust_show_settings() {
339        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
340            TheId::named("Show Settings"),
341            TheWidgetState::Clicked,
342        ));
343    }
344
345    #[unsafe(no_mangle)]
346    pub extern "C" fn rust_show_rules() {
347        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
348            TheId::named("Show Rules"),
349            TheWidgetState::Clicked,
350        ));
351    }
352
353    #[unsafe(no_mangle)]
354    pub extern "C" fn rust_show_locales() {
355        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
356            TheId::named("Show Locales"),
357            TheWidgetState::Clicked,
358        ));
359    }
360
361    #[unsafe(no_mangle)]
362    pub extern "C" fn rust_show_audio_fx() {
363        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
364            TheId::named("Show Audio FX"),
365            TheWidgetState::Clicked,
366        ));
367    }
368
369    #[unsafe(no_mangle)]
370    pub extern "C" fn rust_show_debug_log() {
371        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
372            TheId::named("Show Debug Log"),
373            TheWidgetState::Clicked,
374        ));
375    }
376
377    #[unsafe(no_mangle)]
378    pub extern "C" fn rust_show_console() {
379        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
380            TheId::named("Show Console"),
381            TheWidgetState::Clicked,
382        ));
383    }
384
385    #[unsafe(no_mangle)]
386    pub extern "C" fn rust_cut() -> *mut c_char {
387        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
388            TheId::named("Cut"),
389            TheWidgetState::Clicked,
390        ));
391        APP.lock()
392            .unwrap()
393            .update_ui(&mut UI.lock().unwrap(), &mut CTX.lock().unwrap());
394
395        if let Some(TheValue::Text(text)) = &CTX.lock().unwrap().ui.clipboard {
396            return CString::new(text.clone()).unwrap().into_raw();
397        }
398        ptr::null_mut()
399    }
400
401    #[unsafe(no_mangle)]
402    pub extern "C" fn rust_copy() -> *mut c_char {
403        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
404            TheId::named("Copy"),
405            TheWidgetState::Clicked,
406        ));
407        APP.lock()
408            .unwrap()
409            .update_ui(&mut UI.lock().unwrap(), &mut CTX.lock().unwrap());
410
411        if let Some(TheValue::Text(text)) = &CTX.lock().unwrap().ui.clipboard {
412            return CString::new(text.clone()).unwrap().into_raw();
413        }
414        ptr::null_mut()
415    }
416
417    /// # Safety
418    #[unsafe(no_mangle)]
419    pub unsafe extern "C" fn rust_paste(p: *const c_char) {
420        let text_str = unsafe { CStr::from_ptr(p) };
421        if let Ok(text) = text_str.to_str() {
422            {
423                let mut ctx = CTX.lock().unwrap();
424                ctx.ui.clipboard = Some(TheValue::Text(text.to_string()));
425                ctx.ui.clipboard_app_type = Some("text/plain".to_string());
426            }
427
428            CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
429                TheId::named("Paste"),
430                TheWidgetState::Clicked,
431            ));
432
433            APP.lock()
434                .unwrap()
435                .update_ui(&mut UI.lock().unwrap(), &mut CTX.lock().unwrap());
436        }
437    }
438
439    #[unsafe(no_mangle)]
440    pub extern "C" fn rust_undo() {
441        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
442            TheId::named("Undo"),
443            TheWidgetState::Clicked,
444        ));
445    }
446
447    #[unsafe(no_mangle)]
448    pub extern "C" fn rust_redo() {
449        CTX.lock().unwrap().ui.send(TheEvent::StateChanged(
450            TheId::named("Redo"),
451            TheWidgetState::Clicked,
452        ));
453    }
454
455    #[unsafe(no_mangle)]
456    pub extern "C" fn rust_has_changes() -> bool {
457        APP.lock().unwrap().has_changes()
458    }
459}