diff --git a/examples/runtest.rs b/examples/runtest.rs index 45cd9cd1..ee5b23ae 100644 --- a/examples/runtest.rs +++ b/examples/runtest.rs @@ -1,18 +1,18 @@ //! This is NOT a real example. This is a test designed to see if we can actually run the turtle //! process +// To run, use the command: cargo run --features unstable --example runtest +#[cfg(all(not(feature = "unstable")))] +compile_error!("This example relies on unstable features. Run with `--features unstable`"); -use std::process; - -use turtle::Turtle; +use turtle::Drawing; fn main() { - let mut turtle = Turtle::new(); + let mut drawing = Drawing::new(); + let mut turtle = drawing.add_turtle(); turtle.set_speed(2); turtle.right(90.0); turtle.forward(50.0); - //TODO: Exiting the process currently doesn't cause the window to get closed. We should add a - // `close(self)` or `quit(self)` method to `Drawing` that closes the window explicitly. - process::exit(0); + drawing.destroy(); } diff --git a/src/async_drawing.rs b/src/async_drawing.rs index 42931ea1..499ef40b 100644 --- a/src/async_drawing.rs +++ b/src/async_drawing.rs @@ -187,4 +187,8 @@ impl AsyncDrawing { pub async fn debug(&self) -> impl Debug { self.client.debug_drawing().await } + + pub fn destroy(self) { + self.client.destroy(); + } } diff --git a/src/drawing.rs b/src/drawing.rs index 014e9eb1..37d01282 100644 --- a/src/drawing.rs +++ b/src/drawing.rs @@ -627,6 +627,32 @@ impl Drawing { pub fn save_svg>(&self, path: P) -> Result<(), ExportError> { block_on(self.drawing.save_svg(path)) } + + /// Destroys underlying window and drops self. + /// + /// Subsequent commands to turtle, created using [`Drawing::add_turtle`], might panic. + /// + /// ```rust + /// use turtle::Drawing; + /// + /// let mut drawing = Drawing::new(); + /// let mut turtle = drawing.add_turtle(); + /// + /// turtle.set_speed(2); + /// turtle.right(90.0); + /// turtle.forward(50.0); + /// + /// // close window + /// drawing.destroy(); + /// + /// // this will panic! + /// // turtle.forward(100.0) + /// ``` + #[cfg(feature = "unstable")] + #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))] + pub fn destroy(self) { + self.drawing.destroy(); + } } #[cfg(test)] diff --git a/src/ipc_protocol/messages.rs b/src/ipc_protocol/messages.rs index 0fdb1f18..ceb975eb 100644 --- a/src/ipc_protocol/messages.rs +++ b/src/ipc_protocol/messages.rs @@ -152,6 +152,11 @@ pub enum ClientRequest { /// /// Response: `ServerResponse::DebugDrawing` DebugDrawing, + + /// Destroys drawing window. + /// + /// Response: N/A + DestroyDrawing, } #[derive(Debug, Serialize, Deserialize)] diff --git a/src/ipc_protocol/protocol.rs b/src/ipc_protocol/protocol.rs index d389773b..0ec25550 100644 --- a/src/ipc_protocol/protocol.rs +++ b/src/ipc_protocol/protocol.rs @@ -422,4 +422,8 @@ impl ProtocolClient { _ => unreachable!("bug: expected to receive `DebugDrawing` in response to `DebugDrawing` request"), } } + + pub fn destroy(self) { + self.client.send(ClientRequest::DestroyDrawing); + } } diff --git a/src/renderer_server.rs b/src/renderer_server.rs index 04fdad80..54b90354 100644 --- a/src/renderer_server.rs +++ b/src/renderer_server.rs @@ -157,6 +157,10 @@ fn dispatch_request( DebugDrawing => { handlers::debug_drawing(conn, &app.read()) }, + + DestroyDrawing => { + handlers::destroy_drawing(event_loop) + }, } } diff --git a/src/renderer_server/event_loop_notifier.rs b/src/renderer_server/event_loop_notifier.rs index 5010b2e3..9be612fd 100644 --- a/src/renderer_server/event_loop_notifier.rs +++ b/src/renderer_server/event_loop_notifier.rs @@ -27,6 +27,8 @@ pub enum MainThreadAction { SetIsMaximized(bool), /// Change the fullscreen state of the window SetIsFullscreen(bool), + /// Exit event loop (close window) + Exit, } /// Notifies the main loop when actions need to take place @@ -60,6 +62,10 @@ impl EventLoopNotifier { self.send_action(MainThreadAction::SetIsFullscreen(is_fullscreen)) } + pub fn exit(&self) -> Result<(), EventLoopClosed> { + self.send_action(MainThreadAction::Exit) + } + fn send_action(&self, action: MainThreadAction) -> Result<(), EventLoopClosed> { Ok(self.event_loop.send_event(action)?) } diff --git a/src/renderer_server/handlers.rs b/src/renderer_server/handlers.rs index 3c8fda10..11167e20 100644 --- a/src/renderer_server/handlers.rs +++ b/src/renderer_server/handlers.rs @@ -7,6 +7,7 @@ mod animation; mod fill; mod clear; mod debug; +mod destroy_drawing; pub(crate) use create_turtle::*; pub(crate) use export_drawings::*; @@ -17,6 +18,7 @@ pub(crate) use animation::*; pub(crate) use fill::*; pub(crate) use clear::*; pub(crate) use debug::*; +pub(crate) use destroy_drawing::*; use thiserror::Error; diff --git a/src/renderer_server/handlers/destroy_drawing.rs b/src/renderer_server/handlers/destroy_drawing.rs new file mode 100644 index 00000000..a99d1dc4 --- /dev/null +++ b/src/renderer_server/handlers/destroy_drawing.rs @@ -0,0 +1,7 @@ +use super::super::event_loop_notifier::EventLoopNotifier; +use super::HandlerError; + +pub(crate) fn destroy_drawing(event_loop: &EventLoopNotifier) -> Result<(), HandlerError> { + event_loop.exit()?; + Ok(()) +} diff --git a/src/renderer_server/main.rs b/src/renderer_server/main.rs index de0c81ce..2b4ba320 100644 --- a/src/renderer_server/main.rs +++ b/src/renderer_server/main.rs @@ -255,6 +255,10 @@ pub fn run_main( } else { None }); }, + GlutinEvent::UserEvent(MainThreadAction::Exit) => { + *control_flow = ControlFlow::Exit; + } + GlutinEvent::RedrawRequested(_) => { // Check if we just rendered let last_render_delay = last_render.elapsed(); diff --git a/src/renderer_server/test_event_loop_notifier.rs b/src/renderer_server/test_event_loop_notifier.rs index c12ee61b..637fe518 100644 --- a/src/renderer_server/test_event_loop_notifier.rs +++ b/src/renderer_server/test_event_loop_notifier.rs @@ -33,4 +33,8 @@ impl EventLoopNotifier { pub fn set_is_fullscreen(&self, _is_fullscreen: bool) -> Result<(), EventLoopClosed> { Ok(()) } + + pub fn exit(&self) -> Result<(), EventLoopClosed> { + Ok(()) + } }