diff --git a/src/editor/main.rs b/src/editor/main.rs index ada93534..15a5c313 100644 --- a/src/editor/main.rs +++ b/src/editor/main.rs @@ -117,6 +117,7 @@ macro_rules! action { #[allow(unused_mut)] fn main() { + // This RefCell is only a minor layer of safety in a wildly unsafe setup let mut state_cell = RefCell::new(State::new()); macro_rules! state { () => {&mut *state_cell.borrow_mut()} @@ -359,60 +360,75 @@ impl State { } unsafe fn load_env(&mut self, path: PathBuf) { + let self_ptr: *mut Self = self; + let window_ptr = self.widgets.window; + println!("Environment: {}", path.display()); - let mut preprocessor; - match dm::preprocessor::Preprocessor::new(path.clone()) { - Err(_) => return show_error(self.widgets.window(), &format!("Could not open for reading:\n{}", path.display())), - Ok(pp) => preprocessor = pp, - }; + let path2 = path.clone(); + qt::future::spawn(move || -> Option { + let mut preprocessor; + match dm::preprocessor::Preprocessor::new(path2.clone()) { + Err(_) => { + show_error(&mut *static_cast_mut(window_ptr), &format!("Could not open for reading:\n{}", path2.display())); + return None; + }, + Ok(pp) => preprocessor = pp, + }; - let objtree; - match dm::parser::parse(dm::indents::IndentProcessor::new(&mut preprocessor)) { - Err(e) => { - let mut message = format!("\ - Could not parse the environment:\n\ - {}\n\n\ - This may be caused by incorrect or unusual code, but is typically a parser bug. \ - Change the code to use a more common form, or report the parsing problem.\n\ - ", path.display()); - let mut message_buf = Vec::new(); - let _ = dm::pretty_print_error(&mut message_buf, &preprocessor, &e); - message.push_str(&String::from_utf8_lossy(&message_buf[..])); - return show_error(self.widgets.window(), &message); - }, - Ok(t) => objtree = t, - } - - // fill the object tree - { - let widget = self.widgets.tree(); - widget.clear(); - let root = objtree.root(); - for &root_child in ["area", "turf", "obj", "mob"].iter() { - let ty = root.child(root_child, &objtree).expect("builtins missing"); - - let mut root_item = TreeWidgetItem::new(()); - root_item.set_text(0, qstr!(&ty.name)); - add_children(&mut root_item, ty, &objtree); - widget.add_top_level_item(qt_own!(root_item)); + match dm::parser::parse(dm::indents::IndentProcessor::new(&mut preprocessor)) { + Err(e) => { + let mut message = format!("\ + Could not parse the environment:\n\ + {}\n\n\ + This may be caused by incorrect or unusual code, but is typically a parser bug. \ + Change the code to use a more common form, or report the parsing problem.\n\ + ", path2.display()); + let mut message_buf = Vec::new(); + let _ = dm::pretty_print_error(&mut message_buf, &preprocessor, &e); + message.push_str(&String::from_utf8_lossy(&message_buf[..])); + show_error(&mut *static_cast_mut(window_ptr), &message); + None + }, + Ok(t) => Some(t) } - } - self.config.make_recent(&path); - self.update_recent(); - self.config.save(); + }, move |objtree| { + let this = &mut *self_ptr; + let objtree = match objtree { + Some(t) => t, + None => return + }; - self.env = Some(Environment { - root: path.parent().unwrap().to_owned(), - dme: path, - objtree: objtree, + // fill the object tree + { + let widget = this.widgets.tree(); + widget.clear(); + let root = objtree.root(); + for &root_child in ["area", "turf", "obj", "mob"].iter() { + let ty = root.child(root_child, &objtree).expect("builtins missing"); + + let mut root_item = TreeWidgetItem::new(()); + root_item.set_text(0, qstr!(&ty.name)); + add_children(&mut root_item, ty, &objtree); + widget.add_top_level_item(qt_own!(root_item)); + } + } + this.config.make_recent(&path); + this.update_recent(); + this.config.save(); + + this.env = Some(Environment { + root: path.parent().unwrap().to_owned(), + dme: path, + objtree: objtree, + }); + + // un-disable the actions + let actions = this.widgets.menu_file().actions(); + for i in 0..actions.count() { + (**actions.at(i)).set_disabled(false); + } }); - - // un-disable the actions - let actions = self.widgets.menu_file().actions(); - for i in 0..actions.count() { - (**actions.at(i)).set_disabled(false); - } } unsafe fn new_map(&mut self) { diff --git a/src/qt-extras/future.rs b/src/qt-extras/future.rs new file mode 100644 index 00000000..765b5093 --- /dev/null +++ b/src/qt-extras/future.rs @@ -0,0 +1,65 @@ +use std::sync::mpsc; +use libc::c_void; + +// doesn't check for Send or Sync +pub unsafe fn spawn(task: F1, callback: F2) where + F1: 'static + FnOnce() -> R, + F2: 'static + FnOnce(R), +{ + let (tx, rx) = mpsc::channel(); + let (task, task_data) = make_callback(move || { + let _ = tx.send(task()); + }); + let (cb, cb_data) = make_callback(move || { + if let Ok(data) = rx.try_recv() { + callback(data); + } + }); + qt_spawn_future(task, task_data, cb, cb_data); +} + +unsafe fn make_callback(f: F) -> (callback_fn, *mut c_void) { + unsafe extern "C" fn callback(ptr: *mut c_void) { + Box::from_raw(ptr as *mut F)(); + } + (callback::, Box::into_raw(Box::new(f)) as *mut c_void) +} + +#[allow(non_camel_case_types)] +type callback_fn = unsafe extern "C" fn(*mut c_void); +extern { + fn qt_spawn_future(task: callback_fn, task_data: *mut c_void, cb: callback_fn, cb_data: *mut c_void); +} + +cpp! {{ + #include + #include + #include + + extern "C" typedef void (*callback_fn)(void*); + + class RustSignaller: public QFutureWatcher { + callback_fn callback; + void* data; + void handleFinished() { + callback(data); + delete this; + } + + public: + RustSignaller(callback_fn callback, void* data) + : callback(callback) + , data(data) + { + QObject::connect(this, &QFutureWatcher::finished, this, &RustSignaller::handleFinished); + } + }; + + extern "C" void qt_spawn_future(callback_fn task, void* taskData, callback_fn cb, void* cbData) { + RustSignaller* signaller = new RustSignaller(cb, cbData); + QFuture future = QtConcurrent::run([=]() { + task(taskData); + }); + signaller->setFuture(future); + } +}} diff --git a/src/qt-extras/lib.rs b/src/qt-extras/lib.rs index 35902b86..81bc2972 100644 --- a/src/qt-extras/lib.rs +++ b/src/qt-extras/lib.rs @@ -6,6 +6,7 @@ pub extern crate cpp_utils; #[macro_use] extern crate cpp; pub mod glium_widget; +pub mod future; pub use widgets::qt_core as core; pub use widgets::qt_gui as gui;