From 56d091fb6f5aaeeb01c32418517d3d01b0bcc69a Mon Sep 17 00:00:00 2001
From: Wesley Moore <wes@wezm.net>
Date: Sat, 26 Nov 2022 20:38:41 +1000
Subject: Reduce CPU usage by waiting for an event

---
 src/app.rs | 642 +++++++++++++++++++++++++++++++------------------------------
 1 file changed, 327 insertions(+), 315 deletions(-)

(limited to 'src')

diff --git a/src/app.rs b/src/app.rs
index 3e7ac0f..6f39b5e 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -28,6 +28,7 @@ use std::{
 };
 
 use obi::{CompressionType, Image};
+use sdl2::mouse::MouseState;
 use sdl2::{
     event::Event,
     keyboard::{Keycode, Mod},
@@ -73,6 +74,11 @@ pub struct AppState<'ctx> {
     pub pan_start: Point,
 }
 
+enum HandleEventOutcome {
+    Normal,
+    Break,
+}
+
 // private actions on appstate
 impl<'ctx> AppState<'ctx> {
     fn pan<P: Into<Point>>(&mut self, direction: P) {
@@ -737,354 +743,360 @@ impl<'ctx> AppState<'ctx> {
         'running: loop {
             let mouse = event_pump.mouse_state();
             self.mouse = (mouse.x(), mouse.y());
+            let event = event_pump.wait_event();
+            match self.handle_event(mouse, event) {
+                HandleEventOutcome::Normal => {}
+                HandleEventOutcome::Break => break,
+            }
             for event in event_pump.poll_iter() {
-                if let Event::KeyDown {
-                    keycode: Some(Keycode::Num9),
-                    keymod,
-                    ..
-                } = event
-                {
-                    if keymod == Mod::LSHIFTMOD || keymod == Mod::RSHIFTMOD {
-                        self.mode = Mode::Command;
-                    }
+                match self.handle_event(mouse, event) {
+                    HandleEventOutcome::Normal => {}
+                    HandleEventOutcome::Break => break 'running,
                 }
-                match self.mode {
-                    Mode::Draw => {
-                        match event {
-                            Event::KeyDown {
-                                keycode: Some(k),
-                                keymod: Mod::NOMOD,
-                                ..
-                            } => {
-                                match k {
-                                    // pan
-                                    Keycode::W => self.pan((0, 10)),
-                                    Keycode::A => self.pan((10, 0)),
-                                    Keycode::S => self.pan((0, -10)),
-                                    Keycode::D => self.pan((-10, 0)),
-                                    // zoom
-                                    // Keycode::C if keymod == Mod::LSHIFTMOD => {
-                                    //     self.center_grid();
-                                    // }
-                                    Keycode::C => {
-                                        let cursor = (mouse.x(), mouse.y());
-                                        self.zoom_in(cursor);
-                                    }
-                                    Keycode::Z => {
-                                        let cursor = (mouse.x(), mouse.y());
-                                        self.zoom_out(cursor);
-                                    }
-                                    // brush ops
-                                    Keycode::Q => self.brush.shrink(),
-                                    Keycode::E => self.brush.grow(),
-                                    Keycode::Num1 => self.reduce_intensity(),
-                                    Keycode::Num3 => self.increase_intensity(),
-                                    // flip color
-                                    Keycode::X => self.change_active_color(),
-                                    // toggle grid
-                                    Keycode::Tab => self.toggle_grid(),
-                                    // invert canvas
-                                    Keycode::I => {
-                                        self.pixmap.invert();
-                                        self.undo_stack.push(ModifyRecord::Invert);
-                                    }
-                                    // cycle through brushes
-                                    Keycode::F => self.cycle_brush(),
-                                    // change rect select active end
-                                    Keycode::O => {
-                                        match &mut self.brush {
-                                            Brush::RectSelect(RectSelectBrush {
-                                                active_end,
-                                                ..
-                                            }) => *active_end = !*active_end,
-                                            _ => (),
-                                        };
-                                    }
-                                    Keycode::V => self.cycle_symmetry(),
-                                    // undo
-                                    Keycode::U => {
-                                        if let Some(op) = self.undo_stack.undo() {
-                                            self.apply_operation(op, OpKind::Undo);
-                                        }
-                                    }
-                                    // redo
-                                    Keycode::R => {
-                                        if let Some(op) = self.undo_stack.redo() {
-                                            self.apply_operation(op, OpKind::Redo);
-                                        }
-                                    }
-                                    Keycode::Escape => {
-                                        match self.brush {
-                                            Brush::RectSelect(_) => self.brush = Brush::rect(),
-                                            _ => (),
-                                        }
-                                        continue;
+            }
+            self.cache();
+            self.redraw();
+        }
+    }
+
+    #[must_use]
+    fn handle_event(&mut self, mouse: MouseState, event: Event) -> HandleEventOutcome {
+        if let Event::KeyDown {
+            keycode: Some(Keycode::Num9),
+            keymod,
+            ..
+        } = event
+        {
+            if keymod == Mod::LSHIFTMOD || keymod == Mod::RSHIFTMOD {
+                self.mode = Mode::Command;
+            }
+        }
+        match self.mode {
+            Mode::Draw => {
+                match event {
+                    Event::KeyDown {
+                        keycode: Some(k),
+                        keymod: Mod::NOMOD,
+                        ..
+                    } => {
+                        match k {
+                            // pan
+                            Keycode::W => self.pan((0, 10)),
+                            Keycode::A => self.pan((10, 0)),
+                            Keycode::S => self.pan((0, -10)),
+                            Keycode::D => self.pan((-10, 0)),
+                            // zoom
+                            // Keycode::C if keymod == Mod::LSHIFTMOD => {
+                            //     self.center_grid();
+                            // }
+                            Keycode::C => {
+                                let cursor = (mouse.x(), mouse.y());
+                                self.zoom_in(cursor);
+                            }
+                            Keycode::Z => {
+                                let cursor = (mouse.x(), mouse.y());
+                                self.zoom_out(cursor);
+                            }
+                            // brush ops
+                            Keycode::Q => self.brush.shrink(),
+                            Keycode::E => self.brush.grow(),
+                            Keycode::Num1 => self.reduce_intensity(),
+                            Keycode::Num3 => self.increase_intensity(),
+                            // flip color
+                            Keycode::X => self.change_active_color(),
+                            // toggle grid
+                            Keycode::Tab => self.toggle_grid(),
+                            // invert canvas
+                            Keycode::I => {
+                                self.pixmap.invert();
+                                self.undo_stack.push(ModifyRecord::Invert);
+                            }
+                            // cycle through brushes
+                            Keycode::F => self.cycle_brush(),
+                            // change rect select active end
+                            Keycode::O => {
+                                match &mut self.brush {
+                                    Brush::RectSelect(RectSelectBrush { active_end, .. }) => {
+                                        *active_end = !*active_end
                                     }
                                     _ => (),
+                                };
+                            }
+                            Keycode::V => self.cycle_symmetry(),
+                            // undo
+                            Keycode::U => {
+                                if let Some(op) = self.undo_stack.undo() {
+                                    self.apply_operation(op, OpKind::Undo);
                                 }
                             }
-                            Event::KeyDown {
-                                keycode: Some(k),
-                                keymod,
-                                ..
-                            } if self.keybinds.contains_key(&Keybind::new(k, keymod)) => {
-                                let body =
-                                // clone here because body can modify itself
-                                    self.keybinds.get(&Keybind::new(k, keymod)).unwrap().clone();
-                                self.eval_expr(&body);
+                            // redo
+                            Keycode::R => {
+                                if let Some(op) = self.undo_stack.redo() {
+                                    self.apply_operation(op, OpKind::Redo);
+                                }
                             }
-                            Event::KeyDown {
-                                keycode: Some(k), ..
-                            } if k == Keycode::LCtrl || k == Keycode::RCtrl => {
-                                self.brush = Brush::line(
-                                    self.cache
-                                        .borrow()
-                                        .as_ref()
-                                        .map(|c| c.last_brush.size().unwrap_or(0))
-                                        .unwrap_or(0),
-                                    true,
-                                );
+                            Keycode::Escape => {
+                                match self.brush {
+                                    Brush::RectSelect(_) => self.brush = Brush::rect(),
+                                    _ => (),
+                                }
+                                return HandleEventOutcome::Normal;
                             }
-                            Event::KeyUp {
-                                keycode: Some(k), ..
-                            } if k == Keycode::LCtrl || k == Keycode::RCtrl => {
-                                self.brush = Brush::new(
-                                    self.cache
-                                        .borrow()
-                                        .as_ref()
-                                        .map(|c| c.last_brush.size().unwrap_or(0))
-                                        .unwrap_or(0),
-                                );
+                            _ => (),
+                        }
+                    }
+                    Event::KeyDown {
+                        keycode: Some(k),
+                        keymod,
+                        ..
+                    } if self.keybinds.contains_key(&Keybind::new(k, keymod)) => {
+                        let body =
+                            // clone here because body can modify itself
+                            self.keybinds.get(&Keybind::new(k, keymod)).unwrap().clone();
+                        self.eval_expr(&body);
+                    }
+                    Event::KeyDown {
+                        keycode: Some(k), ..
+                    } if k == Keycode::LCtrl || k == Keycode::RCtrl => {
+                        self.brush = Brush::line(
+                            self.cache
+                                .borrow()
+                                .as_ref()
+                                .map(|c| c.last_brush.size().unwrap_or(0))
+                                .unwrap_or(0),
+                            true,
+                        );
+                    }
+                    Event::KeyUp {
+                        keycode: Some(k), ..
+                    } if k == Keycode::LCtrl || k == Keycode::RCtrl => {
+                        self.brush = Brush::new(
+                            self.cache
+                                .borrow()
+                                .as_ref()
+                                .map(|c| c.last_brush.size().unwrap_or(0))
+                                .unwrap_or(0),
+                        );
+                    }
+                    Event::MouseButtonDown {
+                        x,
+                        y,
+                        mouse_btn: MouseButton::Middle,
+                        ..
+                    } => self.pan_start = Point::from((x, y)),
+                    // start of operation
+                    Event::MouseButtonDown {
+                        x, y, mouse_btn, ..
+                    } => {
+                        let pt = (x, y);
+                        let contact = self.idx_at_coord(pt).map(MapPoint::from);
+                        let val = match mouse_btn {
+                            MouseButton::Right => !self.active_color,
+                            _ => self.active_color,
+                        };
+                        match self.brush {
+                            Brush::Circle(CircleBrush { size }) => {
+                                if let Ok(o) = self.paint_point(pt, val, size) {
+                                    self.current_operation.extend(o);
+                                }
                             }
-                            Event::MouseButtonDown {
-                                x,
-                                y,
-                                mouse_btn: MouseButton::Middle,
-                                ..
-                            } => self.pan_start = Point::from((x, y)),
-                            // start of operation
-                            Event::MouseButtonDown {
-                                x, y, mouse_btn, ..
-                            } => {
-                                let pt = (x, y);
-                                let contact = self.idx_at_coord(pt).map(MapPoint::from);
-                                let val = match mouse_btn {
-                                    MouseButton::Right => !self.active_color,
-                                    _ => self.active_color,
-                                };
-                                match self.brush {
-                                    Brush::Circle(CircleBrush { size }) => {
-                                        if let Ok(o) = self.paint_point(pt, val, size) {
-                                            self.current_operation.extend(o);
-                                        }
+                            Brush::Line(LineBrush {
+                                size,
+                                start,
+                                extend,
+                            }) => {
+                                if let Some(s) = start {
+                                    if let Ok(o) = self.paint_line(s, pt, val, size) {
+                                        self.current_operation.extend(o);
+                                        self.brush = Brush::Line(LineBrush {
+                                            size,
+                                            start: if extend { contact } else { None },
+                                            extend,
+                                        });
                                     }
-                                    Brush::Line(LineBrush {
+                                } else {
+                                    self.brush = Brush::Line(LineBrush {
                                         size,
-                                        start,
+                                        start: contact,
                                         extend,
-                                    }) => {
-                                        if let Some(s) = start {
-                                            if let Ok(o) = self.paint_line(s, pt, val, size) {
-                                                self.current_operation.extend(o);
-                                                self.brush = Brush::Line(LineBrush {
-                                                    size,
-                                                    start: if extend { contact } else { None },
-                                                    extend,
-                                                });
-                                            }
-                                        } else {
-                                            self.brush = Brush::Line(LineBrush {
-                                                size,
-                                                start: contact,
-                                                extend,
-                                            });
-                                        }
-                                    }
-                                    Brush::RectSelect(RectSelectBrush {
+                                    });
+                                }
+                            }
+                            Brush::RectSelect(RectSelectBrush {
+                                start,
+                                end,
+                                active_end,
+                            }) => {
+                                if start.is_none() {
+                                    self.brush = Brush::RectSelect(RectSelectBrush {
+                                        start: contact,
+                                        end,
+                                        active_end,
+                                    });
+                                } else if end.is_none() || active_end {
+                                    self.brush = Brush::RectSelect(RectSelectBrush {
                                         start,
+                                        end: contact,
+                                        active_end,
+                                    });
+                                } else {
+                                    self.brush = Brush::RectSelect(RectSelectBrush {
+                                        start: contact,
                                         end,
                                         active_end,
-                                    }) => {
-                                        if start.is_none() {
-                                            self.brush = Brush::RectSelect(RectSelectBrush {
-                                                start: contact,
-                                                end,
-                                                active_end,
-                                            });
-                                        } else if end.is_none() || active_end {
-                                            self.brush = Brush::RectSelect(RectSelectBrush {
-                                                start,
-                                                end: contact,
-                                                active_end,
-                                            });
-                                        } else {
-                                            self.brush = Brush::RectSelect(RectSelectBrush {
-                                                start: contact,
-                                                end,
-                                                active_end,
-                                            });
-                                        };
-                                    }
-                                    Brush::Fill => {
-                                        if let Some(c) = contact {
-                                            // this `get` is unchecked because contact is checked
-                                            // to be within pixmap
-                                            let target = self.pixmap.get(c);
-                                            let replacement = self.active_color;
-                                            let operation =
-                                                self.pixmap.flood_fill(c, target, replacement);
-                                            for o in operation.iter() {
-                                                // this `set` is unchecked because the returned
-                                                // value of flood_fill is checked to be within pixmap
-                                                self.pixmap.set(*o, replacement);
-                                            }
-                                            self.current_operation.extend(
-                                                operation
-                                                    .into_iter()
-                                                    .map(|point| PaintRecord {
-                                                        point,
-                                                        old: target,
-                                                        new: replacement,
-                                                    })
-                                                    .collect::<Vec<PaintRecord>>(),
-                                            )
-                                        }
+                                    });
+                                };
+                            }
+                            Brush::Fill => {
+                                if let Some(c) = contact {
+                                    // this `get` is unchecked because contact is checked
+                                    // to be within pixmap
+                                    let target = self.pixmap.get(c);
+                                    let replacement = self.active_color;
+                                    let operation = self.pixmap.flood_fill(c, target, replacement);
+                                    for o in operation.iter() {
+                                        // this `set` is unchecked because the returned
+                                        // value of flood_fill is checked to be within pixmap
+                                        self.pixmap.set(*o, replacement);
                                     }
-                                    _ => {}
+                                    self.current_operation.extend(
+                                        operation
+                                            .into_iter()
+                                            .map(|point| PaintRecord {
+                                                point,
+                                                old: target,
+                                                new: replacement,
+                                            })
+                                            .collect::<Vec<PaintRecord>>(),
+                                    )
                                 }
                             }
-                            // click and drag
-                            Event::MouseMotion {
-                                x, y, mousestate, ..
-                            } => {
-                                if mousestate.is_mouse_button_pressed(MouseButton::Left) {
-                                    match self.brush {
-                                        Brush::RectSelect(RectSelectBrush {
+                            _ => {}
+                        }
+                    }
+                    // click and drag
+                    Event::MouseMotion {
+                        x, y, mousestate, ..
+                    } => {
+                        if mousestate.is_mouse_button_pressed(MouseButton::Left) {
+                            match self.brush {
+                                Brush::RectSelect(RectSelectBrush {
+                                    start,
+                                    end,
+                                    active_end,
+                                }) => {
+                                    if active_end {
+                                        self.brush = Brush::RectSelect(RectSelectBrush {
                                             start,
+                                            end: self.idx_at_coord((x, y)).map(MapPoint::from),
+                                            active_end,
+                                        });
+                                    } else {
+                                        self.brush = Brush::RectSelect(RectSelectBrush {
+                                            start: self.idx_at_coord((x, y)).map(MapPoint::from),
                                             end,
                                             active_end,
-                                        }) => {
-                                            if active_end {
-                                                self.brush = Brush::RectSelect(RectSelectBrush {
-                                                    start,
-                                                    end: self
-                                                        .idx_at_coord((x, y))
-                                                        .map(MapPoint::from),
-                                                    active_end,
-                                                });
-                                            } else {
-                                                self.brush = Brush::RectSelect(RectSelectBrush {
-                                                    start: self
-                                                        .idx_at_coord((x, y))
-                                                        .map(MapPoint::from),
-                                                    end,
-                                                    active_end,
-                                                });
-                                            }
-                                        }
-                                        Brush::Circle(CircleBrush { size }) => {
-                                            let pt = (x, y);
-                                            let val = self.active_color;
-                                            if let Ok(o) = self.paint_point(pt, val, size) {
-                                                self.current_operation.extend(o);
-                                            }
-                                        }
-                                        _ => (),
+                                        });
                                     }
-                                } else if mousestate.is_mouse_button_pressed(MouseButton::Right) {
-                                    match self.brush {
-                                        Brush::Circle(CircleBrush { size })
-                                        | Brush::Line(LineBrush { size, .. }) => {
-                                            let pt = (x, y);
-                                            let val = !self.active_color;
-                                            if let Ok(o) = self.paint_point(pt, val, size) {
-                                                self.current_operation.extend(o);
-                                            }
-                                        }
-                                        _ => (),
+                                }
+                                Brush::Circle(CircleBrush { size }) => {
+                                    let pt = (x, y);
+                                    let val = self.active_color;
+                                    if let Ok(o) = self.paint_point(pt, val, size) {
+                                        self.current_operation.extend(o);
                                     }
-                                } else if mousestate.is_mouse_button_pressed(MouseButton::Middle) {
-                                    let pan_end = Point::from((x, y));
-                                    let shift = pan_end - self.pan_start;
-                                    self.start += shift;
-                                    self.pan_start = pan_end;
                                 }
+                                _ => (),
                             }
-                            // end of operation
-                            Event::MouseButtonUp { .. } => self.commit_operation(),
-                            Event::Quit { .. } => {
-                                break 'running;
+                        } else if mousestate.is_mouse_button_pressed(MouseButton::Right) {
+                            match self.brush {
+                                Brush::Circle(CircleBrush { size })
+                                | Brush::Line(LineBrush { size, .. }) => {
+                                    let pt = (x, y);
+                                    let val = !self.active_color;
+                                    if let Ok(o) = self.paint_point(pt, val, size) {
+                                        self.current_operation.extend(o);
+                                    }
+                                }
+                                _ => (),
                             }
-                            _ => {}
+                        } else if mousestate.is_mouse_button_pressed(MouseButton::Middle) {
+                            let pan_end = Point::from((x, y));
+                            let shift = pan_end - self.pan_start;
+                            self.start += shift;
+                            self.pan_start = pan_end;
                         }
                     }
-                    Mode::Command => {
-                        if let Event::KeyDown {
-                            keycode, keymod, ..
-                        } = event
-                        {
-                            let video = self.context.video().unwrap();
-                            let clipboard = video.clipboard();
-                            if is_copy_event(keycode, keymod) {
-                                clipboard
-                                    .set_clipboard_text(&self.command_box.text)
-                                    .unwrap();
-                            } else if is_paste_event(keycode, keymod)
-                                && clipboard.has_clipboard_text()
-                            {
-                                self.command_box
-                                    .push_str(&clipboard.clipboard_text().unwrap());
-                            }
+                    // end of operation
+                    Event::MouseButtonUp { .. } => self.commit_operation(),
+                    Event::Quit { .. } => {
+                        return HandleEventOutcome::Break;
+                    }
+                    _ => {}
+                }
+            }
+            Mode::Command => {
+                if let Event::KeyDown {
+                    keycode, keymod, ..
+                } = event
+                {
+                    let video = self.context.video().unwrap();
+                    let clipboard = video.clipboard();
+                    if is_copy_event(keycode, keymod) {
+                        clipboard
+                            .set_clipboard_text(&self.command_box.text)
+                            .unwrap();
+                    } else if is_paste_event(keycode, keymod) && clipboard.has_clipboard_text() {
+                        self.command_box
+                            .push_str(&clipboard.clipboard_text().unwrap());
+                    }
+                }
+                match event {
+                    Event::KeyDown {
+                        keycode: Some(k),
+                        keymod,
+                        ..
+                    } => match k {
+                        Keycode::Backspace => self.command_box.backspace(),
+                        Keycode::Delete => self.command_box.delete(),
+                        Keycode::Left => self.command_box.backward(),
+                        Keycode::Right => self.command_box.forward(),
+                        Keycode::Up => self.command_box.hist_prev(),
+                        Keycode::Down => self.command_box.hist_next(),
+                        Keycode::Return => self.eval_command(),
+                        Keycode::Tab if keymod == Mod::LSHIFTMOD => {
+                            self.command_box.complete(&self.lisp_env, true)
                         }
-                        match event {
-                            Event::KeyDown {
-                                keycode: Some(k),
-                                keymod,
-                                ..
-                            } => match k {
-                                Keycode::Backspace => self.command_box.backspace(),
-                                Keycode::Delete => self.command_box.delete(),
-                                Keycode::Left => self.command_box.backward(),
-                                Keycode::Right => self.command_box.forward(),
-                                Keycode::Up => self.command_box.hist_prev(),
-                                Keycode::Down => self.command_box.hist_next(),
-                                Keycode::Return => self.eval_command(),
-                                Keycode::Tab if keymod == Mod::LSHIFTMOD => {
-                                    self.command_box.complete(&self.lisp_env, true)
-                                }
-                                Keycode::Tab => self.command_box.complete(&self.lisp_env, false),
-                                Keycode::Escape => {
-                                    self.command_box.clear();
-                                    self.message.text.clear();
-                                    self.mode = Mode::Draw;
-                                }
-                                _ if keymod == Mod::LCTRLMOD => match k {
-                                    Keycode::A => self.command_box.cursor_start(),
-                                    Keycode::E => self.command_box.cursor_end(),
-                                    Keycode::F => self.command_box.forward(),
-                                    Keycode::B => self.command_box.backward(),
-                                    Keycode::K => self.command_box.delete_to_end(),
-                                    Keycode::U => self.command_box.delete_to_start(),
-                                    _ => (),
-                                },
-                                // how does one handle alt keys
-                                _ if keymod == Mod::LALTMOD => match k {
-                                    Keycode::B => self.command_box.cursor_back_word(),
-                                    Keycode::F => self.command_box.cursor_forward_word(),
-                                    _ => (),
-                                },
-                                _ => (),
-                            },
-                            Event::TextInput { text, .. } => {
-                                self.command_box.push_str(&text[..]);
-                            }
-                            _ => (),
+                        Keycode::Tab => self.command_box.complete(&self.lisp_env, false),
+                        Keycode::Escape => {
+                            self.command_box.clear();
+                            self.message.text.clear();
+                            self.mode = Mode::Draw;
                         }
+                        _ if keymod == Mod::LCTRLMOD => match k {
+                            Keycode::A => self.command_box.cursor_start(),
+                            Keycode::E => self.command_box.cursor_end(),
+                            Keycode::F => self.command_box.forward(),
+                            Keycode::B => self.command_box.backward(),
+                            Keycode::K => self.command_box.delete_to_end(),
+                            Keycode::U => self.command_box.delete_to_start(),
+                            _ => (),
+                        },
+                        // how does one handle alt keys
+                        _ if keymod == Mod::LALTMOD => match k {
+                            Keycode::B => self.command_box.cursor_back_word(),
+                            Keycode::F => self.command_box.cursor_forward_word(),
+                            _ => (),
+                        },
+                        _ => (),
+                    },
+                    Event::TextInput { text, .. } => {
+                        self.command_box.push_str(&text[..]);
                     }
+                    _ => (),
                 }
             }
-            self.cache();
-            self.redraw();
         }
+        HandleEventOutcome::Normal
     }
 }
-- 
cgit v1.2.3