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