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::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::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#[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 APP.lock().unwrap().set_cmd_line_args(
118 vec!["eldiron-creator".to_string()],
119 &mut CTX.lock().unwrap(),
120 );
121 }
122
123 #[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 #[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 #[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 #[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 #[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}