Added mouse support, but it's not used yet

This commit is contained in:
transistor 2022-10-02 20:39:02 -07:00
parent 4cb423d85a
commit a9b8633531
10 changed files with 202 additions and 25 deletions

3
.gitignore vendored
View File

@ -1,6 +1,6 @@
Cargo.lock Cargo.lock
.*.sw? .*.sw?
/target target
*.vim *.vim
junk/ junk/
@ -9,3 +9,4 @@ perf.data.old
binaries/*/*.asm binaries/*/*.asm
binaries/*/*.bin binaries/*/*.bin
binaries/*/*.smd

View File

@ -1,7 +1,6 @@
use std::mem; use std::mem;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::collections::VecDeque;
use crate::Clock; use crate::Clock;
use crate::Error; use crate::Error;

View File

@ -110,3 +110,17 @@ pub enum Key {
Unknown, Unknown,
} }
pub struct KeyEvent {
pub key: Key,
pub state: bool,
}
impl KeyEvent {
pub fn new(key: Key, state: bool) -> Self {
Self {
key,
state,
}
}
}

View File

@ -2,11 +2,13 @@
mod traits; mod traits;
mod keys; mod keys;
mod controllers; mod controllers;
mod mouse;
pub mod gfx; pub mod gfx;
pub mod audio; 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::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};

View File

@ -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<usize> 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<MouseButton> 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<MouseEvent> {
if *self != next_state {
self.pos = next_state.pos;
let events: Vec<MouseEvent> = 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 => { },
}
}
}

View File

@ -3,9 +3,10 @@ use std::collections::VecDeque;
use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::{Arc, Mutex, MutexGuard};
use crate::{Clock, Error}; use crate::{Clock, Error};
use crate::host::keys::Key;
use crate::host::gfx::Frame; use crate::host::gfx::Frame;
use crate::host::keys::KeyEvent;
use crate::host::controllers::{ControllerDevice, ControllerEvent}; use crate::host::controllers::{ControllerDevice, ControllerEvent};
use crate::host::mouse::MouseEvent;
pub trait Host { pub trait Host {
fn create_pty(&self) -> Result<Box<dyn Tty>, Error> { fn create_pty(&self) -> Result<Box<dyn Tty>, Error> {
@ -24,6 +25,10 @@ pub trait Host {
Err(Error::new("This frontend doesn't support the keyboard")) Err(Error::new("This frontend doesn't support the keyboard"))
} }
fn register_mouse(&mut self, _input: Box<dyn MouseUpdater>) -> Result<(), Error> {
Err(Error::new("This frontend doesn't support the mouse"))
}
fn create_audio_source(&mut self) -> Result<Box<dyn Audio>, Error> { fn create_audio_source(&mut self) -> Result<Box<dyn Audio>, Error> {
Err(Error::new("This frontend doesn't support the sound")) Err(Error::new("This frontend doesn't support the sound"))
} }
@ -56,7 +61,11 @@ pub trait ControllerUpdater: Send {
} }
pub trait KeyboardUpdater: 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 { pub trait Audio {

View File

@ -5,25 +5,31 @@ use moa_minifb;
use moa_peripherals_yamaha::{Ym2612, Sn76489}; use moa_peripherals_yamaha::{Ym2612, Sn76489};
use moa_core::host::gfx::{Frame, FrameQueue}; 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}; 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<KeyEvent>);
impl KeyboardUpdater for SynthControlsUpdater { impl KeyboardUpdater for SynthControlsUpdater {
fn update_keyboard(&mut self, key: Key, state: bool) { fn update_keyboard(&mut self, event: KeyEvent) {
self.0.send((key, state)).unwrap(); self.0.send(event).unwrap();
} }
} }
//impl MouseUpdater for SynthControlsUpdater {
// fn update_mouse(&mut self, event: MouseEvent) {
// self.0.send(event).unwrap();
// }
//}
struct SynthControl { struct SynthControl {
queue: FrameQueue, queue: FrameQueue,
receiver: mpsc::Receiver<(Key, bool)>, receiver: mpsc::Receiver<KeyEvent>,
} }
impl SynthControl { impl SynthControl {
pub fn new(queue: FrameQueue, receiver: mpsc::Receiver<(Key, bool)>) -> Self { pub fn new(queue: FrameQueue, receiver: mpsc::Receiver<KeyEvent>) -> Self {
Self { Self {
queue, queue,
receiver, receiver,
@ -33,18 +39,18 @@ impl SynthControl {
impl Steppable for SynthControl { impl Steppable for SynthControl {
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> { fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
if let Ok((key, state)) = self.receiver.try_recv() { if let Ok(event) = self.receiver.try_recv() {
match key { match event.key {
Key::Enter => { Key::Enter => {
system.get_bus().write_u8(0x00, 0x28)?; 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 => { Key::A => {
system.get_bus().write_u8(0x10, 0x84)?; system.get_bus().write_u8(0x10, 0x84)?;
system.get_bus().write_u8(0x10, 0x0F)?; 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.add_window(Box::new(queue.clone()))?;
host.register_keyboard(Box::new(SynthControlsUpdater(sender)))?; host.register_keyboard(Box::new(SynthControlsUpdater(sender)))?;
//host.register_mouse(Box::new(SynthControlsUpdater(sender)))?;
Ok(system) Ok(system)
}); });

View File

@ -4,11 +4,11 @@ use std::str::FromStr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use minifb::{self, Key}; use minifb::{self, Key, MouseMode, MouseButton};
use clap::{App, Arg, ArgMatches}; use clap::{App, Arg, ArgMatches};
use moa_core::{System, Error, Clock}; 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_core::host::gfx::Frame;
use moa_common::audio::{AudioOutput, AudioMixer, AudioSource}; use moa_common::audio::{AudioOutput, AudioMixer, AudioSource};
@ -98,6 +98,7 @@ pub struct MiniFrontendBuilder {
pub window: Option<Box<dyn WindowUpdater>>, pub window: Option<Box<dyn WindowUpdater>>,
pub controller: Option<Box<dyn ControllerUpdater>>, pub controller: Option<Box<dyn ControllerUpdater>>,
pub keyboard: Option<Box<dyn KeyboardUpdater>>, pub keyboard: Option<Box<dyn KeyboardUpdater>>,
pub mouse: Option<Box<dyn MouseUpdater>>,
pub mixer: Option<HostData<AudioMixer>>, pub mixer: Option<HostData<AudioMixer>>,
pub finalized: bool, pub finalized: bool,
} }
@ -108,6 +109,7 @@ impl MiniFrontendBuilder {
window: None, window: None,
controller: None, controller: None,
keyboard: None, keyboard: None,
mouse: None,
mixer: Some(AudioMixer::new_default()), mixer: Some(AudioMixer::new_default()),
finalized: false, finalized: false,
} }
@ -121,8 +123,9 @@ impl MiniFrontendBuilder {
let window = std::mem::take(&mut self.window); let window = std::mem::take(&mut self.window);
let controller = std::mem::take(&mut self.controller); let controller = std::mem::take(&mut self.controller);
let keyboard = std::mem::take(&mut self.keyboard); let keyboard = std::mem::take(&mut self.keyboard);
let mouse = std::mem::take(&mut self.mouse);
let mixer = std::mem::take(&mut self.mixer); 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(()) Ok(())
} }
fn register_mouse(&mut self, input: Box<dyn MouseUpdater>) -> 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<Box<dyn Audio>, Error> { fn create_audio_source(&mut self) -> Result<Box<dyn Audio>, Error> {
let source = AudioSource::new(self.mixer.as_ref().unwrap().clone()); let source = AudioSource::new(self.mixer.as_ref().unwrap().clone());
Ok(Box::new(source)) Ok(Box::new(source))
@ -164,20 +175,30 @@ impl Host for MiniFrontendBuilder {
pub struct MiniFrontend { pub struct MiniFrontend {
pub modifiers: u16, pub modifiers: u16,
pub mouse_state: MouseState,
pub window: Option<Box<dyn WindowUpdater>>, pub window: Option<Box<dyn WindowUpdater>>,
pub controller: Option<Box<dyn ControllerUpdater>>, pub controller: Option<Box<dyn ControllerUpdater>>,
pub keyboard: Option<Box<dyn KeyboardUpdater>>, pub keyboard: Option<Box<dyn KeyboardUpdater>>,
pub mouse: Option<Box<dyn MouseUpdater>>,
pub audio: Option<AudioOutput>, pub audio: Option<AudioOutput>,
pub mixer: HostData<AudioMixer>, pub mixer: HostData<AudioMixer>,
} }
impl MiniFrontend { impl MiniFrontend {
pub fn new(window: Option<Box<dyn WindowUpdater>>, controller: Option<Box<dyn ControllerUpdater>>, keyboard: Option<Box<dyn KeyboardUpdater>>, mixer: HostData<AudioMixer>) -> Self { pub fn new(
window: Option<Box<dyn WindowUpdater>>,
controller: Option<Box<dyn ControllerUpdater>>,
keyboard: Option<Box<dyn KeyboardUpdater>>,
mouse: Option<Box<dyn MouseUpdater>>,
mixer: HostData<AudioMixer>,
) -> Self {
Self { Self {
modifiers: 0, modifiers: 0,
mouse_state: Default::default(),
window, window,
controller, controller,
keyboard, keyboard,
mouse,
audio: None, audio: None,
mixer, 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 Some(updater) = self.window.as_mut() {
if let Ok(frame) = updater.take_frame() { if let Ok(frame) = updater.take_frame() {
last_frame = frame last_frame = frame
@ -270,7 +305,7 @@ impl MiniFrontend {
fn check_key(&mut self, key: Key, state: bool) { fn check_key(&mut self, key: Key, state: bool) {
if let Some(updater) = self.keyboard.as_mut() { 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() { if let Some(updater) = self.controller.as_mut() {

View File

@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex};
use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable, debug, warning}; use moa_core::{System, Error, ClockElapsed, Address, Addressable, Steppable, Transmutable, debug, warning};
use moa_core::host::gfx::{Frame, FrameQueue}; 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::keymap;
use super::charset::CharacterGenerator; use super::charset::CharacterGenerator;
@ -37,9 +37,9 @@ impl Model1Peripherals {
pub struct Model1KeyboardUpdater(Arc<Mutex<[u8; 8]>>); pub struct Model1KeyboardUpdater(Arc<Mutex<[u8; 8]>>);
impl KeyboardUpdater for Model1KeyboardUpdater { impl KeyboardUpdater for Model1KeyboardUpdater {
fn update_keyboard(&mut self, key: Key, state: bool) { fn update_keyboard(&mut self, event: KeyEvent) {
println!(">>> {:?}", key); println!(">>> {:?}", event.key);
keymap::record_key_press(&mut self.0.lock().unwrap(), key, state); keymap::record_key_press(&mut self.0.lock().unwrap(), event.key, event.state);
} }
} }

View File

@ -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 * test the Z80 more, add tests
* double check the functioning of the banked areas and register settings for Z80 coprocessor * 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 * 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