input.rsannotatedinput.rssource207 lines · 6.6 KB · raw
1use bincode::{Decode, Encode};
2use jgenesis_common::frontend::{DisplayInfo, FiniteF64, MappableInputs, Modal};
3use jgenesis_common::input::Player;
4use snes_config::{SnesButton, SnesJoypadState, SuperScopeButton};
5
6pub(crate) trait SnesJoypadStateExt: Sized + Copy {
7    #[must_use]
8    fn to_register_word(self) -> u16;
9}
10
11impl SnesJoypadStateExt for SnesJoypadState {
12    fn to_register_word(self) -> u16 {
13        u16::from(self.b)
14            | (u16::from(self.y) << 1)
15            | (u16::from(self.select) << 2)
16            | (u16::from(self.start) << 3)
17            | (u16::from(self.up) << 4)
18            | (u16::from(self.down) << 5)
19            | (u16::from(self.left) << 6)
20            | (u16::from(self.right) << 7)
21            | (u16::from(self.a) << 8)
22            | (u16::from(self.x) << 9)
23            | (u16::from(self.l) << 10)
24            | (u16::from(self.r) << 11)
25    }
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Encode, Decode)]
29pub struct SnesMouseState {
30    // Absolute position values are meaningless, only differences between positions are meaningful
31    pub x_position: FiniteF64,
32    pub y_position: FiniteF64,
33    pub left: bool,
34    pub right: bool,
35}
36
37impl SnesMouseState {
38    #[inline]
39    pub fn set_button(&mut self, button: SnesButton, pressed: bool) {
40        match button {
41            SnesButton::MouseLeft => self.left = pressed,
42            SnesButton::MouseRight => self.right = pressed,
43            _ => {}
44        }
45    }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
49pub struct SuperScopeState {
50    pub fire: bool,
51    pub cursor: bool,
52    pub pause: bool,
53    pub turbo: bool,
54    // X/Y position in SNES pixels starting from the top-left corner, or None if position is offscreen
55    // X should be in the range 0..=255 and Y should be in the range 0..=223 (or 238 if in 239-line mode); other values
56    // will be treated as offscreen
57    pub position: Option<(u16, u16)>,
58}
59
60impl Default for SuperScopeState {
61    fn default() -> Self {
62        Self { fire: false, cursor: false, pause: false, turbo: true, position: None }
63    }
64}
65
66impl SuperScopeState {
67    #[inline]
68    pub fn set_button(&mut self, button: SuperScopeButton, pressed: bool) {
69        match button {
70            SuperScopeButton::Fire => self.fire = pressed,
71            SuperScopeButton::Cursor => self.cursor = pressed,
72            SuperScopeButton::Pause => self.pause = pressed,
73            SuperScopeButton::TurboToggle => {
74                if pressed {
75                    self.turbo = !self.turbo;
76                }
77            }
78        }
79    }
80
81    #[inline]
82    #[must_use]
83    pub fn with_button(mut self, button: SuperScopeButton, pressed: bool) -> Self {
84        self.set_button(button, pressed);
85        self
86    }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
90pub enum SnesController {
91    Gamepad(SnesJoypadState),
92    Mouse(SnesMouseState),
93    SuperScope(SuperScopeState),
94    None,
95}
96
97impl SnesController {
98    pub fn set_field(&mut self, button: SnesButton, pressed: bool) {
99        match self {
100            Self::Gamepad(state) => state.set_button(button, pressed),
101            Self::Mouse(state) => state.set_button(button, pressed),
102            Self::SuperScope(state) => {
103                if let Some(super_scope_button) = button.to_super_scope() {
104                    state.set_button(super_scope_button, pressed);
105                }
106            }
107            Self::None => {}
108        }
109    }
110
111    #[must_use]
112    pub fn with_allow_opposing_directions(self, allow_opposing_directions: bool) -> Self {
113        match self {
114            Self::Gamepad(joypad) => {
115                Self::Gamepad(joypad.with_allow_opposing_directions(allow_opposing_directions))
116            }
117            Self::Mouse(_) | Self::SuperScope(_) | Self::None => self,
118        }
119    }
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
123pub struct SnesInputs {
124    pub p1: SnesController,
125    pub p2: SnesController,
126}
127
128impl Default for SnesInputs {
129    fn default() -> Self {
130        Self { p1: SnesController::Gamepad(SnesJoypadState::default()), p2: SnesController::None }
131    }
132}
133
134impl MappableInputs<SnesButton> for SnesInputs {
135    #[inline]
136    fn set_field(&mut self, button: SnesButton, player: Player, pressed: bool) {
137        match player {
138            Player::One => self.p1.set_field(button, pressed),
139            Player::Two => self.p2.set_field(button, pressed),
140            _ => {}
141        }
142    }
143
144    #[inline]
145    fn handle_mouse_motion(
146        &mut self,
147        (x, y): (f32, f32),
148        (dx, dy): (f32, f32),
149        display_info: DisplayInfo,
150    ) {
151        for controller in [&mut self.p1, &mut self.p2] {
152            match controller {
153                SnesController::Mouse(mouse_state) => {
154                    if let Ok(dx) = FiniteF64::try_from(f64::from(dx)) {
155                        mouse_state.x_position += dx;
156                    }
157                    if let Ok(dy) = FiniteF64::try_from(f64::from(dy)) {
158                        mouse_state.y_position += dy;
159                    }
160                }
161                SnesController::SuperScope(super_scope_state) => {
162                    super_scope_state.position =
163                        jgenesis_common::input::viewport_position_to_frame_position(
164                            x,
165                            y,
166                            display_info,
167                        );
168                    log::debug!("Set Super Scope position to {:?}", super_scope_state.position);
169                }
170                _ => {}
171            }
172        }
173    }
174
175    #[inline]
176    fn handle_mouse_leave(&mut self) {
177        for controller in [&mut self.p1, &mut self.p2] {
178            if let SnesController::SuperScope(super_scope_state) = controller {
179                super_scope_state.position = None;
180            }
181        }
182    }
183
184    #[inline]
185    fn needs_relative_mouse_mode(&self) -> bool {
186        [self.p1, self.p2]
187            .into_iter()
188            .any(|controller| matches!(controller, SnesController::Mouse(_)))
189    }
190
191    fn modal_for_input(&self, button: SnesButton, player: Player, pressed: bool) -> Option<Modal> {
192        if button != SnesButton::SuperScopeTurboToggle || !pressed {
193            return None;
194        }
195
196        let controller = match player {
197            Player::One => &self.p1,
198            Player::Two => &self.p2,
199            _ => return None,
200        };
201        let SnesController::SuperScope(super_scope_state) = controller else { return None };
202
203        let text =
204            format!("Super Scope Turbo: {}", if super_scope_state.turbo { "On" } else { "Off" });
205        Some(Modal { id: Some("super_scope_turbo".into()), text })
206    }
207}