mirror of
https://github.com/transistorfet/moa.git
synced 2024-12-22 12:29:51 +00:00
Added mouse support, but it's not used yet
This commit is contained in:
parent
4cb423d85a
commit
a9b8633531
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,6 +1,6 @@
|
||||
Cargo.lock
|
||||
.*.sw?
|
||||
/target
|
||||
target
|
||||
*.vim
|
||||
junk/
|
||||
|
||||
@ -9,3 +9,4 @@ perf.data.old
|
||||
|
||||
binaries/*/*.asm
|
||||
binaries/*/*.bin
|
||||
binaries/*/*.smd
|
||||
|
@ -1,7 +1,6 @@
|
||||
|
||||
use std::mem;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::Clock;
|
||||
use crate::Error;
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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};
|
||||
|
||||
|
106
emulator/core/src/host/mouse.rs
Normal file
106
emulator/core/src/host/mouse.rs
Normal 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 => { },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Box<dyn Tty>, 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<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> {
|
||||
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 {
|
||||
|
@ -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<KeyEvent>);
|
||||
|
||||
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<KeyEvent>,
|
||||
}
|
||||
|
||||
impl SynthControl {
|
||||
pub fn new(queue: FrameQueue, receiver: mpsc::Receiver<(Key, bool)>) -> Self {
|
||||
pub fn new(queue: FrameQueue, receiver: mpsc::Receiver<KeyEvent>) -> Self {
|
||||
Self {
|
||||
queue,
|
||||
receiver,
|
||||
@ -33,18 +39,18 @@ impl SynthControl {
|
||||
|
||||
impl Steppable for SynthControl {
|
||||
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 => {
|
||||
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)
|
||||
});
|
||||
|
@ -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<Box<dyn WindowUpdater>>,
|
||||
pub controller: Option<Box<dyn ControllerUpdater>>,
|
||||
pub keyboard: Option<Box<dyn KeyboardUpdater>>,
|
||||
pub mouse: Option<Box<dyn MouseUpdater>>,
|
||||
pub mixer: Option<HostData<AudioMixer>>,
|
||||
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<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> {
|
||||
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<Box<dyn WindowUpdater>>,
|
||||
pub controller: Option<Box<dyn ControllerUpdater>>,
|
||||
pub keyboard: Option<Box<dyn KeyboardUpdater>>,
|
||||
pub mouse: Option<Box<dyn MouseUpdater>>,
|
||||
pub audio: Option<AudioOutput>,
|
||||
pub mixer: HostData<AudioMixer>,
|
||||
}
|
||||
|
||||
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 {
|
||||
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() {
|
||||
|
@ -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<Mutex<[u8; 8]>>);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
4
todo.txt
4
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
|
||||
|
Loading…
Reference in New Issue
Block a user