From a9b8633531b50731f14d1a72c97cf6b60518450b Mon Sep 17 00:00:00 2001 From: transistor Date: Sun, 2 Oct 2022 20:39:02 -0700 Subject: [PATCH] Added mouse support, but it's not used yet --- .gitignore | 3 +- emulator/core/src/host/gfx.rs | 1 - emulator/core/src/host/keys.rs | 14 +++ emulator/core/src/host/mod.rs | 6 +- emulator/core/src/host/mouse.rs | 106 ++++++++++++++++++ emulator/core/src/host/traits.rs | 13 ++- .../frontends/minifb/src/bin/moa-synth.rs | 27 +++-- emulator/frontends/minifb/src/lib.rs | 45 +++++++- .../systems/trs80/src/peripherals/model1.rs | 8 +- todo.txt | 4 + 10 files changed, 202 insertions(+), 25 deletions(-) create mode 100644 emulator/core/src/host/mouse.rs diff --git a/.gitignore b/.gitignore index 21bef9f..0f03ea3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ Cargo.lock .*.sw? -/target +target *.vim junk/ @@ -9,3 +9,4 @@ perf.data.old binaries/*/*.asm binaries/*/*.bin +binaries/*/*.smd diff --git a/emulator/core/src/host/gfx.rs b/emulator/core/src/host/gfx.rs index 60e1fda..f5dfa72 100644 --- a/emulator/core/src/host/gfx.rs +++ b/emulator/core/src/host/gfx.rs @@ -1,7 +1,6 @@ use std::mem; use std::sync::{Arc, Mutex}; -use std::collections::VecDeque; use crate::Clock; use crate::Error; diff --git a/emulator/core/src/host/keys.rs b/emulator/core/src/host/keys.rs index c7a98ad..694e5d2 100644 --- a/emulator/core/src/host/keys.rs +++ b/emulator/core/src/host/keys.rs @@ -110,3 +110,17 @@ pub enum Key { Unknown, } +pub struct KeyEvent { + pub key: Key, + pub state: bool, +} + +impl KeyEvent { + pub fn new(key: Key, state: bool) -> Self { + Self { + key, + state, + } + } +} + diff --git a/emulator/core/src/host/mod.rs b/emulator/core/src/host/mod.rs index d5c225c..8d274c7 100644 --- a/emulator/core/src/host/mod.rs +++ b/emulator/core/src/host/mod.rs @@ -2,11 +2,13 @@ mod traits; mod keys; mod controllers; +mod mouse; pub mod gfx; pub mod audio; -pub use self::keys::Key; +pub use self::keys::{Key, KeyEvent}; +pub use self::mouse::{MouseButton, MouseEventType, MouseEvent, MouseState}; pub use self::controllers::{ControllerDevice, ControllerEvent}; -pub use self::traits::{Host, Tty, WindowUpdater, ControllerUpdater, KeyboardUpdater, Audio, BlitableSurface, HostData, DummyAudio}; +pub use self::traits::{Host, Tty, WindowUpdater, ControllerUpdater, KeyboardUpdater, MouseUpdater, Audio, BlitableSurface, HostData, DummyAudio}; diff --git a/emulator/core/src/host/mouse.rs b/emulator/core/src/host/mouse.rs new file mode 100644 index 0000000..08a69e7 --- /dev/null +++ b/emulator/core/src/host/mouse.rs @@ -0,0 +1,106 @@ + +pub enum MouseButton { + Left, + Right, + Middle, +} + +pub enum MouseEventType { + Down(MouseButton), + Up(MouseButton), + Move, +} + +pub struct MouseEvent { + pub etype: MouseEventType, + pub pos: (u32, u32), +} + +#[derive(Clone, Default, PartialEq, Eq)] +pub struct MouseState { + pub buttons: [bool; 3], + pub pos: (u32, u32), +} + +impl MouseEvent { + pub fn new(etype: MouseEventType, pos: (u32, u32)) -> Self { + Self { + etype, + pos, + } + } +} + +impl From for MouseButton { + fn from(index: usize) -> MouseButton { + match index { + 0 => MouseButton::Left, + 1 => MouseButton::Right, + 2 => MouseButton::Middle, + _ => panic!("unexpected mouse button index: {:?}", index), + } + } +} + +impl From for usize { + fn from(button: MouseButton) -> usize { + match button { + MouseButton::Left => 0, + MouseButton::Right => 1, + MouseButton::Middle => 2, + } + } +} + +impl MouseState { + pub fn with(left: bool, right: bool, middle: bool, x: u32, y: u32) -> Self { + Self { + buttons: [left, right, middle], + pos: (x, y), + } + } + + pub fn to_events(&mut self, next_state: MouseState) -> Vec { + if *self != next_state { + self.pos = next_state.pos; + + let events: Vec = self + .buttons.into_iter() + .zip(next_state.buttons.into_iter()) + .enumerate() + .filter_map(|(i, (prev, next))| { + if prev != next { + self.buttons[i] = next; + let button = MouseButton::from(i); + let etype = if next { + MouseEventType::Down(button) + } else { + MouseEventType::Up(button) + }; + Some(MouseEvent::new(etype, next_state.pos)) + } else { + None + } + }) + .collect(); + + if !events.is_empty() { + events + } else { + vec![MouseEvent::new(MouseEventType::Move, next_state.pos)] + } + } else { + vec![] + } + } + + pub fn update_with(&mut self, event: MouseEvent) { + self.pos = event.pos; + match event.etype { + MouseEventType::Up(button) => self.buttons[usize::from(button)] = false, + MouseEventType::Down(button) => self.buttons[usize::from(button)] = true, + MouseEventType::Move => { }, + } + } +} + diff --git a/emulator/core/src/host/traits.rs b/emulator/core/src/host/traits.rs index 77dce7a..68fbd48 100644 --- a/emulator/core/src/host/traits.rs +++ b/emulator/core/src/host/traits.rs @@ -3,9 +3,10 @@ use std::collections::VecDeque; use std::sync::{Arc, Mutex, MutexGuard}; use crate::{Clock, Error}; -use crate::host::keys::Key; use crate::host::gfx::Frame; +use crate::host::keys::KeyEvent; use crate::host::controllers::{ControllerDevice, ControllerEvent}; +use crate::host::mouse::MouseEvent; pub trait Host { fn create_pty(&self) -> Result, Error> { @@ -24,6 +25,10 @@ pub trait Host { Err(Error::new("This frontend doesn't support the keyboard")) } + fn register_mouse(&mut self, _input: Box) -> Result<(), Error> { + Err(Error::new("This frontend doesn't support the mouse")) + } + fn create_audio_source(&mut self) -> Result, Error> { Err(Error::new("This frontend doesn't support the sound")) } @@ -56,7 +61,11 @@ pub trait ControllerUpdater: Send { } pub trait KeyboardUpdater: Send { - fn update_keyboard(&mut self, key: Key, state: bool); + fn update_keyboard(&mut self, event: KeyEvent); +} + +pub trait MouseUpdater: Send { + fn update_mouse(&mut self, event: MouseEvent); } pub trait Audio { diff --git a/emulator/frontends/minifb/src/bin/moa-synth.rs b/emulator/frontends/minifb/src/bin/moa-synth.rs index 8c32815..5cf0716 100644 --- a/emulator/frontends/minifb/src/bin/moa-synth.rs +++ b/emulator/frontends/minifb/src/bin/moa-synth.rs @@ -5,25 +5,31 @@ use moa_minifb; use moa_peripherals_yamaha::{Ym2612, Sn76489}; use moa_core::host::gfx::{Frame, FrameQueue}; -use moa_core::host::{Host, WindowUpdater, KeyboardUpdater, Key}; +use moa_core::host::{Host, WindowUpdater, KeyboardUpdater, Key, KeyEvent, MouseUpdater, MouseState, MouseEvent}; use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable, TransmutableBox, wrap_transmutable}; -pub struct SynthControlsUpdater(mpsc::Sender<(Key, bool)>); +pub struct SynthControlsUpdater(mpsc::Sender); impl KeyboardUpdater for SynthControlsUpdater { - fn update_keyboard(&mut self, key: Key, state: bool) { - self.0.send((key, state)).unwrap(); + fn update_keyboard(&mut self, event: KeyEvent) { + self.0.send(event).unwrap(); } } +//impl MouseUpdater for SynthControlsUpdater { +// fn update_mouse(&mut self, event: MouseEvent) { +// self.0.send(event).unwrap(); +// } +//} + struct SynthControl { queue: FrameQueue, - receiver: mpsc::Receiver<(Key, bool)>, + receiver: mpsc::Receiver, } impl SynthControl { - pub fn new(queue: FrameQueue, receiver: mpsc::Receiver<(Key, bool)>) -> Self { + pub fn new(queue: FrameQueue, receiver: mpsc::Receiver) -> Self { Self { queue, receiver, @@ -33,18 +39,18 @@ impl SynthControl { impl Steppable for SynthControl { fn step(&mut self, system: &System) -> Result { - if let Ok((key, state)) = self.receiver.try_recv() { + if let Ok(event) = self.receiver.try_recv() { - match key { + match event.key { Key::Enter => { system.get_bus().write_u8(0x00, 0x28)?; - system.get_bus().write_u8(0x01, if state { 0xF0 } else { 0x00 })?; + system.get_bus().write_u8(0x01, if event.state { 0xF0 } else { 0x00 })?; }, Key::A => { system.get_bus().write_u8(0x10, 0x84)?; system.get_bus().write_u8(0x10, 0x0F)?; - system.get_bus().write_u8(0x10, if state { 0x90 } else { 0x9F })?; + system.get_bus().write_u8(0x10, if event.state { 0x90 } else { 0x9F })?; }, _ => { }, @@ -108,6 +114,7 @@ fn main() { host.add_window(Box::new(queue.clone()))?; host.register_keyboard(Box::new(SynthControlsUpdater(sender)))?; + //host.register_mouse(Box::new(SynthControlsUpdater(sender)))?; Ok(system) }); diff --git a/emulator/frontends/minifb/src/lib.rs b/emulator/frontends/minifb/src/lib.rs index 58f3ad4..c3cc781 100644 --- a/emulator/frontends/minifb/src/lib.rs +++ b/emulator/frontends/minifb/src/lib.rs @@ -4,11 +4,11 @@ use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; -use minifb::{self, Key}; +use minifb::{self, Key, MouseMode, MouseButton}; use clap::{App, Arg, ArgMatches}; use moa_core::{System, Error, Clock}; -use moa_core::host::{Host, HostData, ControllerUpdater, KeyboardUpdater, WindowUpdater, Audio, ControllerDevice}; +use moa_core::host::{Host, HostData, ControllerUpdater, KeyboardUpdater, KeyEvent, MouseUpdater, MouseState, WindowUpdater, Audio, ControllerDevice}; use moa_core::host::gfx::Frame; use moa_common::audio::{AudioOutput, AudioMixer, AudioSource}; @@ -98,6 +98,7 @@ pub struct MiniFrontendBuilder { pub window: Option>, pub controller: Option>, pub keyboard: Option>, + pub mouse: Option>, pub mixer: Option>, pub finalized: bool, } @@ -108,6 +109,7 @@ impl MiniFrontendBuilder { window: None, controller: None, keyboard: None, + mouse: None, mixer: Some(AudioMixer::new_default()), finalized: false, } @@ -121,8 +123,9 @@ impl MiniFrontendBuilder { let window = std::mem::take(&mut self.window); let controller = std::mem::take(&mut self.controller); let keyboard = std::mem::take(&mut self.keyboard); + let mouse = std::mem::take(&mut self.mouse); let mixer = std::mem::take(&mut self.mixer); - MiniFrontend::new(window, controller, keyboard, mixer.unwrap()) + MiniFrontend::new(window, controller, keyboard, mouse, mixer.unwrap()) } } @@ -155,6 +158,14 @@ impl Host for MiniFrontendBuilder { Ok(()) } + fn register_mouse(&mut self, input: Box) -> Result<(), Error> { + if self.mouse.is_some() { + return Err(Error::new("A mouse updater has already been registered with the frontend")); + } + self.mouse = Some(input); + Ok(()) + } + fn create_audio_source(&mut self) -> Result, Error> { let source = AudioSource::new(self.mixer.as_ref().unwrap().clone()); Ok(Box::new(source)) @@ -164,20 +175,30 @@ impl Host for MiniFrontendBuilder { pub struct MiniFrontend { pub modifiers: u16, + pub mouse_state: MouseState, pub window: Option>, pub controller: Option>, pub keyboard: Option>, + pub mouse: Option>, pub audio: Option, pub mixer: HostData, } impl MiniFrontend { - pub fn new(window: Option>, controller: Option>, keyboard: Option>, mixer: HostData) -> Self { + pub fn new( + window: Option>, + controller: Option>, + keyboard: Option>, + mouse: Option>, + mixer: HostData, + ) -> Self { Self { modifiers: 0, + mouse_state: Default::default(), window, controller, keyboard, + mouse, audio: None, mixer, } @@ -259,6 +280,20 @@ impl MiniFrontend { } } + if let Some(updater) = self.mouse.as_mut() { + if let Some((x, y)) = window.get_mouse_pos(MouseMode::Clamp) { + let left = window.get_mouse_down(MouseButton::Left); + let right = window.get_mouse_down(MouseButton::Right); + let middle = window.get_mouse_down(MouseButton::Middle); + + let next_state = MouseState::with(left, right, middle, x as u32, y as u32); + self.mouse_state + .to_events(next_state) + .into_iter() + .for_each(|event| updater.update_mouse(event)); + } + } + if let Some(updater) = self.window.as_mut() { if let Ok(frame) = updater.take_frame() { last_frame = frame @@ -270,7 +305,7 @@ impl MiniFrontend { fn check_key(&mut self, key: Key, state: bool) { if let Some(updater) = self.keyboard.as_mut() { - updater.update_keyboard(map_key(key), state); + updater.update_keyboard(KeyEvent::new(map_key(key), state)); } if let Some(updater) = self.controller.as_mut() { diff --git a/emulator/systems/trs80/src/peripherals/model1.rs b/emulator/systems/trs80/src/peripherals/model1.rs index 711c901..0303aad 100644 --- a/emulator/systems/trs80/src/peripherals/model1.rs +++ b/emulator/systems/trs80/src/peripherals/model1.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex}; use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable, debug, warning}; use moa_core::host::gfx::{Frame, FrameQueue}; -use moa_core::host::{Host, BlitableSurface, KeyboardUpdater, Key}; +use moa_core::host::{Host, BlitableSurface, KeyboardUpdater, KeyEvent}; use super::keymap; use super::charset::CharacterGenerator; @@ -37,9 +37,9 @@ impl Model1Peripherals { pub struct Model1KeyboardUpdater(Arc>); impl KeyboardUpdater for Model1KeyboardUpdater { - fn update_keyboard(&mut self, key: Key, state: bool) { - println!(">>> {:?}", key); - keymap::record_key_press(&mut self.0.lock().unwrap(), key, state); + fn update_keyboard(&mut self, event: KeyEvent) { + println!(">>> {:?}", event.key); + keymap::record_key_press(&mut self.0.lock().unwrap(), event.key, event.state); } } diff --git a/todo.txt b/todo.txt index e5cf939..f27963d 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,8 @@ +* can you get audio working without the need to lock during an update? Use the ClockedQueue like frames do... but should the queue be used for the mixer-to-output, + the sources to mixer, or both? +* add mouse support, particularly to allow the synth system to have buttons and keys that can be clicked + * test the Z80 more, add tests * double check the functioning of the banked areas and register settings for Z80 coprocessor * address repeater on ym2612 doesn't seem to work the same, when it's on the 68000 device. The Z80 device doesn't have an affect, but maybe it's not being used