keyboard input works on mac gui (LMAO)

This commit is contained in:
Alex Janka 2023-11-27 12:58:13 +11:00
parent bf7c7b9169
commit 560f1d9693
4 changed files with 146 additions and 28 deletions

2
Cargo.lock generated
View file

@ -487,7 +487,7 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cacao"
version = "0.4.0-beta2"
source = "git+https://github.com/italicsjenga/cacao#6144e9f244abfd15687de00b6cfda6b4606f351a"
source = "git+https://github.com/italicsjenga/cacao#4fd93e3faeec5e80aef36779745b3506c9b5017d"
dependencies = [
"bitmask-enum",
"block2 0.2.0-alpha.6",

View file

@ -9,13 +9,16 @@ use std::{
};
use cacao::{
appkit::window::{Window, WindowConfig, WindowDelegate, WindowStyle},
appkit::{
window::{Window, WindowConfig, WindowDelegate, WindowStyle},
Event, EventMask, EventMonitor,
},
core_foundation::base::TCFTypeRef,
};
use cpal::Stream;
use frontend_common::window::{RendererChannel, WindowManager};
use gb_emu_lib::{
connect::{EmulatorMessage, RendererMessage, ResolutionData},
connect::{EmulatorMessage, JoypadButtons, JoypadState, RendererMessage, ResolutionData},
renderer::{RendererBackend, RendererBackendManager, WindowOptions},
};
use raw_window_handle::{
@ -27,8 +30,15 @@ use uuid::Uuid;
use super::{dispatch, AppMessage};
pub enum EmuWindowMessage {
Closing,
Renderer(RendererMessage<[u8; 4]>),
Closing {
id: Uuid,
},
Renderer {
id: Uuid,
renderer_message: RendererMessage<[u8; 4]>,
},
KeyDown(JoypadButtons),
KeyUp(JoypadButtons),
}
pub struct EmulatorHandles {
@ -60,18 +70,101 @@ impl Drop for EmulatorHandles {
}
}
struct ButtonHandler {
_down_monitor: EventMonitor,
_up_monitor: EventMonitor,
}
impl ButtonHandler {
fn new() -> Self {
let _down_monitor = Event::local_monitor(EventMask::KeyDown, |v| {
if let Some(button) = get_buttons(&v) {
dispatch(AppMessage::EmuWindow(EmuWindowMessage::KeyDown(button)));
None
} else {
Some(v)
}
});
let _up_monitor = Event::local_monitor(EventMask::KeyUp, |v| {
if let Some(button) = get_buttons(&v) {
dispatch(AppMessage::EmuWindow(EmuWindowMessage::KeyUp(button)));
None
} else {
Some(v)
}
});
Self {
_down_monitor,
_up_monitor,
}
}
}
trait GetKeyChar {
fn get_key_char(&self) -> char;
}
impl GetKeyChar for JoypadButtons {
fn get_key_char(&self) -> char {
match self {
JoypadButtons::Down => 's',
JoypadButtons::Up => 'w',
JoypadButtons::Left => 'a',
JoypadButtons::Right => 'd',
JoypadButtons::Start => '=',
JoypadButtons::Select => '-',
JoypadButtons::B => ';',
JoypadButtons::A => '\'',
}
}
}
const ALL_BUTTONS: [JoypadButtons; 8] = [
JoypadButtons::Down,
JoypadButtons::Up,
JoypadButtons::Left,
JoypadButtons::Right,
JoypadButtons::Start,
JoypadButtons::Select,
JoypadButtons::B,
JoypadButtons::A,
];
fn get_buttons(event: &Event) -> Option<JoypadButtons> {
let characters = event.characters();
if characters.len() != 1 {
panic!("ok that assumption was wrong lol. event characters CAN be != 1");
}
if event.current_modifier_flags().is_empty() {
for button in ALL_BUTTONS {
if characters.contains(button.get_key_char()) {
return Some(button);
}
}
}
None
}
pub struct CacaoWindowManager {
backend_manager: Arc<RendererBackendManager>,
windows: HashMap<Uuid, Window<CacaoWindow>>,
handles: Option<EmulatorHandles>,
joypad_state: JoypadState,
_button_handler: ButtonHandler,
}
impl CacaoWindowManager {
pub fn new(display_handle: RawDisplayHandle) -> Self {
let _button_handler = ButtonHandler::new();
Self {
backend_manager: Arc::new(RendererBackendManager::new(display_handle)),
windows: HashMap::new(),
handles: None,
joypad_state: Default::default(),
_button_handler,
}
}
@ -89,19 +182,22 @@ impl CacaoWindowManager {
}
}
pub fn message(&mut self, id: Uuid, message: EmuWindowMessage) {
pub fn message(&mut self, message: EmuWindowMessage) {
match message {
EmuWindowMessage::Closing => {
EmuWindowMessage::Closing { id } => {
self.windows.remove(&id);
if self.windows.is_empty() {
let _ = self.handles.take();
}
}
EmuWindowMessage::Renderer(message) => {
EmuWindowMessage::Renderer {
id,
renderer_message,
} => {
if let Some(window) = self.windows.get_mut(&id) {
if let Some(delegate) = window.delegate.as_mut() {
delegate.message(
message,
renderer_message,
Window {
delegate: None,
objc: window.objc.clone(),
@ -110,6 +206,30 @@ impl CacaoWindowManager {
}
}
}
EmuWindowMessage::KeyDown(key) => {
if let Some(handles) = self.handles.as_ref() {
let old_state = self.joypad_state;
self.joypad_state.set(key, true);
if self.joypad_state != old_state {
handles
.sender
.send(EmulatorMessage::JoypadUpdate(self.joypad_state))
.unwrap();
}
}
}
EmuWindowMessage::KeyUp(key) => {
if let Some(handles) = self.handles.as_ref() {
let old_state = self.joypad_state;
self.joypad_state.set(key, false);
if self.joypad_state != old_state {
handles
.sender
.send(EmulatorMessage::JoypadUpdate(self.joypad_state))
.unwrap();
}
}
}
}
}
}
@ -172,17 +292,18 @@ enum MonitorThreadState {
impl MonitorThread {
fn new(receiver: Receiver<RendererMessage<[u8; 4]>>, id: Uuid) -> Self {
let (destroy, destroy_recv) = mpsc::channel();
let thread = std::thread::spawn(move || loop {
if let Ok(message) = receiver.recv() {
dispatch(AppMessage::EmuWindow {
id,
message: EmuWindowMessage::Renderer(message),
})
}
if let Ok(true) = destroy_recv.try_recv() {
return;
}
});
let thread =
std::thread::spawn(move || loop {
if let Ok(renderer_message) = receiver.recv() {
dispatch(AppMessage::EmuWindow(EmuWindowMessage::Renderer {
id,
renderer_message,
}));
}
if let Ok(true) = destroy_recv.try_recv() {
return;
}
});
Self {
state: MonitorThreadState::Live { thread, destroy },
}
@ -292,10 +413,7 @@ impl WindowDelegate for CacaoWindow {
}
fn will_close(&self) {
dispatch(AppMessage::EmuWindow {
id: self.id,
message: EmuWindowMessage::Closing,
})
dispatch(AppMessage::EmuWindow(EmuWindowMessage::Closing { id: self.id }));
}
}

View file

@ -10,7 +10,6 @@ use cacao::notification_center::Dispatcher;
use frontend_common::audio;
use gb_emu_lib::connect::{EmulatorCoreTrait, EmulatorMessage};
use raw_window_handle::{AppKitDisplayHandle, RawDisplayHandle};
use uuid::Uuid;
use self::cacao_window_manager::{CacaoWindowManager, EmuWindowMessage};
use self::preferences::{PreferencesMessage, PreferencesUi};
@ -21,7 +20,7 @@ mod preferences;
pub(crate) enum AppMessage {
Core(CoreMessage),
Preferences(PreferencesMessage),
EmuWindow { id: Uuid, message: EmuWindowMessage },
EmuWindow(EmuWindowMessage),
}
pub(crate) enum CoreMessage {
@ -122,9 +121,9 @@ impl Dispatcher for TwincUiApp {
}
}
}
AppMessage::EmuWindow { id, message } => {
AppMessage::EmuWindow(window_message) => {
if let Ok(mut window_manager) = self.current_game.write() {
window_manager.message(id, message);
window_manager.message(window_message);
}
}
}

View file

@ -22,6 +22,7 @@ pub struct JoypadState {
pub a: bool,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum JoypadButtons {
Down,
Up,