lift panic handling from UI's event to ezgui runner, use also for draw

This commit is contained in:
Dustin Carlino 2018-12-12 14:01:36 -08:00
parent e29ff690f8
commit d61255a4b3
2 changed files with 88 additions and 78 deletions

View File

@ -19,7 +19,6 @@ use sim::{GetDrawAgents, Sim, SimFlags, Tick};
use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::panic;
use std::process;
const MIN_ZOOM_FOR_MOUSEOVER: f64 = 4.0;
@ -42,21 +41,67 @@ pub struct UI {
}
impl GUI<RenderingHints> for UI {
fn event(&mut self, input: UserInput) -> (EventLoopMode, RenderingHints) {
match panic::catch_unwind(panic::AssertUnwindSafe(|| self.inner_event(input))) {
Ok(hints) => (hints.mode, hints),
Err(err) => {
error!("********************************************************************************");
error!("UI broke! Primary sim:");
self.primary.sim.dump_before_abort();
if let Some((s, _)) = &self.secondary {
error!("Secondary sim:");
s.sim.dump_before_abort();
fn event(&mut self, mut input: UserInput) -> (EventLoopMode, RenderingHints) {
let mut hints = RenderingHints {
mode: EventLoopMode::InputOnly,
osd: Text::new(),
suppress_intersection_icon: None,
color_crosswalks: HashMap::new(),
hide_crosswalks: HashSet::new(),
hide_turn_icons: HashSet::new(),
};
// First update the camera and handle zoom
let old_zoom = self.canvas.cam_zoom;
self.canvas.handle_event(&mut input);
let new_zoom = self.canvas.cam_zoom;
self.primary_plugins
.layers_mut()
.handle_zoom(old_zoom, new_zoom);
// Always handle mouseover
if old_zoom >= MIN_ZOOM_FOR_MOUSEOVER && new_zoom < MIN_ZOOM_FOR_MOUSEOVER {
self.primary.current_selection = None;
}
if !self.canvas.is_dragging()
&& input.get_moved_mouse().is_some()
&& new_zoom >= MIN_ZOOM_FOR_MOUSEOVER
{
self.primary.current_selection = self.mouseover_something();
}
// If there's an active plugin, just run it.
if let Some(idx) = self.active_plugin {
if !self.run_plugin(idx, &mut input, &mut hints) {
self.active_plugin = None;
}
} else {
// Run each plugin, short-circuiting if the plugin claimed it was active.
for idx in 0..self.plugins.list.len() + self.primary_plugins.list.len() {
if self.run_plugin(idx, &mut input, &mut hints) {
self.active_plugin = Some(idx);
break;
}
self.save_editor_state();
panic::resume_unwind(err);
}
}
// Can do this at any time.
if input.unimportant_key_pressed(Key::Escape, ROOT_MENU, "quit") {
self.save_editor_state();
self.cs.borrow().save();
info!("Saved color_scheme");
//cpuprofiler::PROFILER.lock().unwrap().stop().unwrap();
process::exit(0);
}
if self.primary.recalculate_current_selection {
self.primary.recalculate_current_selection = false;
self.primary.current_selection = self.mouseover_something();
}
input.populate_osd(&mut hints.osd);
(hints.mode, hints)
}
fn get_mut_canvas(&mut self) -> &mut Canvas {
@ -109,6 +154,17 @@ impl GUI<RenderingHints> for UI {
self.canvas.draw_text(g, hints.osd, BOTTOM_LEFT);
}
fn dump_before_abort(&self) {
error!("********************************************************************************");
error!("UI broke! Primary sim:");
self.primary.sim.dump_before_abort();
if let Some((s, _)) = &self.secondary {
error!("Secondary sim:");
s.sim.dump_before_abort();
}
self.save_editor_state();
}
}
// All of the state that's bound to a specific map+edit has to live here.
@ -281,69 +337,6 @@ impl UI {
ui
}
fn inner_event(&mut self, mut input: UserInput) -> RenderingHints {
let mut hints = RenderingHints {
mode: EventLoopMode::InputOnly,
osd: Text::new(),
suppress_intersection_icon: None,
color_crosswalks: HashMap::new(),
hide_crosswalks: HashSet::new(),
hide_turn_icons: HashSet::new(),
};
// First update the camera and handle zoom
let old_zoom = self.canvas.cam_zoom;
self.canvas.handle_event(&mut input);
let new_zoom = self.canvas.cam_zoom;
self.primary_plugins
.layers_mut()
.handle_zoom(old_zoom, new_zoom);
// Always handle mouseover
if old_zoom >= MIN_ZOOM_FOR_MOUSEOVER && new_zoom < MIN_ZOOM_FOR_MOUSEOVER {
self.primary.current_selection = None;
}
if !self.canvas.is_dragging()
&& input.get_moved_mouse().is_some()
&& new_zoom >= MIN_ZOOM_FOR_MOUSEOVER
{
self.primary.current_selection = self.mouseover_something();
}
// If there's an active plugin, just run it.
if let Some(idx) = self.active_plugin {
if !self.run_plugin(idx, &mut input, &mut hints) {
self.active_plugin = None;
}
} else {
// Run each plugin, short-circuiting if the plugin claimed it was active.
for idx in 0..self.plugins.list.len() + self.primary_plugins.list.len() {
if self.run_plugin(idx, &mut input, &mut hints) {
self.active_plugin = Some(idx);
break;
}
}
}
// Can do this at any time.
if input.unimportant_key_pressed(Key::Escape, ROOT_MENU, "quit") {
self.save_editor_state();
self.cs.borrow().save();
info!("Saved color_scheme");
//cpuprofiler::PROFILER.lock().unwrap().stop().unwrap();
process::exit(0);
}
if self.primary.recalculate_current_selection {
self.primary.recalculate_current_selection = false;
self.primary.current_selection = self.mouseover_something();
}
input.populate_osd(&mut hints.osd);
hints
}
fn mouseover_something(&self) -> Option<ID> {
let pt = self.canvas.get_cursor_in_map_space();

View File

@ -4,11 +4,14 @@ use opengl_graphics::{Filter, GlGraphics, GlyphCache, OpenGL, TextureSettings};
use piston::event_loop::{EventLoop, EventSettings, Events};
use piston::input::RenderEvent;
use piston::window::{Window, WindowSettings};
use std::panic;
pub trait GUI<T> {
fn event(&mut self, input: UserInput) -> (EventLoopMode, T);
fn get_mut_canvas(&mut self) -> &mut Canvas;
fn draw(&self, g: &mut GfxCtx, data: T);
// Will be called if event or draw panics.
fn dump_before_abort(&self) {}
}
#[derive(Clone, Copy, PartialEq)]
@ -40,7 +43,16 @@ pub fn run<T, G: GUI<T>>(mut gui: G, window_title: &str, initial_width: u32, ini
let mut last_event_mode = EventLoopMode::InputOnly;
while let Some(ev) = events.next(&mut window) {
let (new_event_mode, data) = gui.event(UserInput::new(ev.clone()));
let (new_event_mode, data) = match panic::catch_unwind(panic::AssertUnwindSafe(|| {
gui.event(UserInput::new(ev.clone()))
})) {
Ok(pair) => pair,
Err(err) => {
gui.dump_before_abort();
panic::resume_unwind(err);
}
};
// Don't constantly reset the events struct -- only when laziness changes.
if new_event_mode != last_event_mode {
events.set_lazy(new_event_mode == EventLoopMode::InputOnly);
@ -52,7 +64,12 @@ pub fn run<T, G: GUI<T>>(mut gui: G, window_title: &str, initial_width: u32, ini
let mut g = GfxCtx::new(&mut glyphs, g, c);
gui.get_mut_canvas()
.start_drawing(&mut g, window.draw_size());
gui.draw(&mut g, data);
if let Err(err) =
panic::catch_unwind(panic::AssertUnwindSafe(|| gui.draw(&mut g, data)))
{
gui.dump_before_abort();
panic::resume_unwind(err);
}
});
}
}