Skip to main content

godot_bevy/plugins/input/
input_bridge.rs

1use bevy_app::{App, First, Last, Plugin};
2use bevy_ecs::{
3    entity::Entity,
4    message::{MessageReader, MessageWriter},
5    system::ResMut,
6};
7use bevy_input::{
8    ButtonInput, ButtonState, InputPlugin,
9    gestures::PanGesture as BevyPanGesture,
10    keyboard::KeyCode,
11    mouse::{
12        MouseButton as BevyMouseButton, MouseButtonInput as BevyMouseButtonInput,
13        MouseMotion as BevyMouseMotion, MouseScrollUnit, MouseWheel as BevyMouseWheel,
14    },
15};
16
17use crate::plugins::input::events::{
18    KeyboardInput as GodotKeyboardInput, MouseButton as GodotMouseButton,
19    MouseButtonInput as GodotMouseButtonInput, MouseMotion as GodotMouseMotion,
20    PanGestureInput as GodotPanGestureInput,
21};
22
23/// Plugin that bridges godot-bevy's input messages to Bevy's standard input resources.
24// This plugin automatically includes GodotInputEventPlugin as a dependency.
25#[derive(Default)]
26pub struct BevyInputBridgePlugin;
27
28impl Plugin for BevyInputBridgePlugin {
29    fn build(&self, app: &mut App) {
30        // Add the dependency - we need Godot input events to bridge them
31        app.add_plugins(super::events::GodotInputEventPlugin)
32            .add_plugins(InputPlugin)
33            .add_systems(
34                First,
35                (
36                    bridge_keyboard_input,
37                    bridge_mouse_button_input,
38                    bridge_mouse_motion,
39                    bridge_mouse_scroll,
40                    bridge_pan_gesture,
41                ),
42            )
43            .add_systems(Last, clear_keyboard_input);
44    }
45}
46
47fn bridge_keyboard_input(
48    mut keyboard_messages: MessageReader<GodotKeyboardInput>,
49    mut key_code_input: ResMut<ButtonInput<KeyCode>>,
50) {
51    for message in keyboard_messages.read() {
52        // Convert Godot Key to Bevy KeyCode
53        if let Some(bevy_key_code) = godot_key_to_bevy_keycode(message.keycode) {
54            if message.pressed {
55                key_code_input.press(bevy_key_code);
56            } else {
57                key_code_input.release(bevy_key_code);
58            }
59        }
60    }
61}
62
63fn bridge_mouse_button_input(
64    mut mouse_messages: MessageReader<GodotMouseButtonInput>,
65    mut bevy_mouse_button_messages: MessageWriter<BevyMouseButtonInput>,
66) {
67    for message in mouse_messages.read() {
68        // Skip wheel events - they're handled separately in bridge_mouse_scroll
69        if matches!(
70            message.button,
71            GodotMouseButton::WheelUp
72                | GodotMouseButton::WheelDown
73                | GodotMouseButton::WheelLeft
74                | GodotMouseButton::WheelRight
75        ) {
76            continue;
77        }
78
79        let bevy_button = godot_mouse_to_bevy_mouse(message.button);
80        let state = if message.pressed {
81            ButtonState::Pressed
82        } else {
83            ButtonState::Released
84        };
85
86        // Send MouseButtonInput event that Bevy's mouse_button_input_system will process
87        bevy_mouse_button_messages.write(BevyMouseButtonInput {
88            button: bevy_button,
89            state,
90            window: Entity::PLACEHOLDER,
91        });
92    }
93}
94
95fn bridge_mouse_motion(
96    mut mouse_motion_messages: MessageReader<GodotMouseMotion>,
97    mut bevy_mouse_motion_messages: MessageWriter<BevyMouseMotion>,
98) {
99    // Send individual Bevy MouseMotion events - bevy input will handle the accumulation
100    for event in mouse_motion_messages.read() {
101        bevy_mouse_motion_messages.write(BevyMouseMotion { delta: event.delta });
102    }
103}
104
105fn bridge_mouse_scroll(
106    mut mouse_button_messages: MessageReader<GodotMouseButtonInput>,
107    mut bevy_mouse_scroll_messages: MessageWriter<BevyMouseWheel>,
108) {
109    // Send individual Bevy MouseWheel events - bevy input will handle the accumulation
110    for message in mouse_button_messages.read() {
111        match message.button {
112            GodotMouseButton::WheelUp => {
113                bevy_mouse_scroll_messages.write(BevyMouseWheel {
114                    x: 0.0,
115                    y: message.factor,
116                    unit: MouseScrollUnit::Line,
117                    window: Entity::PLACEHOLDER,
118                });
119            }
120            GodotMouseButton::WheelDown => {
121                bevy_mouse_scroll_messages.write(BevyMouseWheel {
122                    x: 0.0,
123                    y: -message.factor,
124                    unit: MouseScrollUnit::Line,
125                    window: Entity::PLACEHOLDER,
126                });
127            }
128            GodotMouseButton::WheelLeft => {
129                bevy_mouse_scroll_messages.write(BevyMouseWheel {
130                    x: -message.factor,
131                    y: 0.0,
132                    unit: MouseScrollUnit::Line,
133                    window: Entity::PLACEHOLDER,
134                });
135            }
136            GodotMouseButton::WheelRight => {
137                bevy_mouse_scroll_messages.write(BevyMouseWheel {
138                    x: message.factor,
139                    y: 0.0,
140                    unit: MouseScrollUnit::Line,
141                    window: Entity::PLACEHOLDER,
142                });
143            }
144            _ => {} // Ignore non-wheel buttons
145        }
146    }
147}
148
149fn bridge_pan_gesture(
150    mut pan_messages: MessageReader<GodotPanGestureInput>,
151    mut bevy_pan_messages: MessageWriter<BevyPanGesture>,
152) {
153    for event in pan_messages.read() {
154        bevy_pan_messages.write(BevyPanGesture(event.delta));
155    }
156}
157
158fn clear_keyboard_input(mut keyboard_input: ResMut<ButtonInput<KeyCode>>) {
159    // Clear just_pressed/just_released states at the end of each frame
160    // This is what Bevy's InputPlugin normally does for gamepads, but we handle keyboard manually
161    keyboard_input.clear();
162}
163
164// Conversion functions
165fn godot_key_to_bevy_keycode(godot_key: godot::global::Key) -> Option<KeyCode> {
166    use KeyCode as BK;
167    use godot::global::Key as GK;
168
169    match godot_key {
170        GK::A => Some(BK::KeyA),
171        GK::B => Some(BK::KeyB),
172        GK::C => Some(BK::KeyC),
173        GK::D => Some(BK::KeyD),
174        GK::E => Some(BK::KeyE),
175        GK::F => Some(BK::KeyF),
176        GK::G => Some(BK::KeyG),
177        GK::H => Some(BK::KeyH),
178        GK::I => Some(BK::KeyI),
179        GK::J => Some(BK::KeyJ),
180        GK::K => Some(BK::KeyK),
181        GK::L => Some(BK::KeyL),
182        GK::M => Some(BK::KeyM),
183        GK::N => Some(BK::KeyN),
184        GK::O => Some(BK::KeyO),
185        GK::P => Some(BK::KeyP),
186        GK::Q => Some(BK::KeyQ),
187        GK::R => Some(BK::KeyR),
188        GK::S => Some(BK::KeyS),
189        GK::T => Some(BK::KeyT),
190        GK::U => Some(BK::KeyU),
191        GK::V => Some(BK::KeyV),
192        GK::W => Some(BK::KeyW),
193        GK::X => Some(BK::KeyX),
194        GK::Y => Some(BK::KeyY),
195        GK::Z => Some(BK::KeyZ),
196
197        GK::KEY_0 => Some(BK::Digit0),
198        GK::KEY_1 => Some(BK::Digit1),
199        GK::KEY_2 => Some(BK::Digit2),
200        GK::KEY_3 => Some(BK::Digit3),
201        GK::KEY_4 => Some(BK::Digit4),
202        GK::KEY_5 => Some(BK::Digit5),
203        GK::KEY_6 => Some(BK::Digit6),
204        GK::KEY_7 => Some(BK::Digit7),
205        GK::KEY_8 => Some(BK::Digit8),
206        GK::KEY_9 => Some(BK::Digit9),
207
208        GK::SPACE => Some(BK::Space),
209        GK::ENTER => Some(BK::Enter),
210        GK::ESCAPE => Some(BK::Escape),
211        GK::BACKSPACE => Some(BK::Backspace),
212        GK::TAB => Some(BK::Tab),
213        GK::SHIFT => Some(BK::ShiftLeft),
214        GK::CTRL => Some(BK::ControlLeft),
215        GK::ALT => Some(BK::AltLeft),
216
217        GK::LEFT => Some(BK::ArrowLeft),
218        GK::RIGHT => Some(BK::ArrowRight),
219        GK::UP => Some(BK::ArrowUp),
220        GK::DOWN => Some(BK::ArrowDown),
221
222        GK::F1 => Some(BK::F1),
223        GK::F2 => Some(BK::F2),
224        GK::F3 => Some(BK::F3),
225        GK::F4 => Some(BK::F4),
226        GK::F5 => Some(BK::F5),
227        GK::F6 => Some(BK::F6),
228        GK::F7 => Some(BK::F7),
229        GK::F8 => Some(BK::F8),
230        GK::F9 => Some(BK::F9),
231        GK::F10 => Some(BK::F10),
232        GK::F11 => Some(BK::F11),
233        GK::F12 => Some(BK::F12),
234
235        // Numpad keys
236        GK::KP_0 => Some(BK::Numpad0),
237        GK::KP_1 => Some(BK::Numpad1),
238        GK::KP_2 => Some(BK::Numpad2),
239        GK::KP_3 => Some(BK::Numpad3),
240        GK::KP_4 => Some(BK::Numpad4),
241        GK::KP_5 => Some(BK::Numpad5),
242        GK::KP_6 => Some(BK::Numpad6),
243        GK::KP_7 => Some(BK::Numpad7),
244        GK::KP_8 => Some(BK::Numpad8),
245        GK::KP_9 => Some(BK::Numpad9),
246        GK::KP_ADD => Some(BK::NumpadAdd),
247        GK::KP_SUBTRACT => Some(BK::NumpadSubtract),
248        GK::KP_MULTIPLY => Some(BK::NumpadMultiply),
249        GK::KP_DIVIDE => Some(BK::NumpadDivide),
250        GK::KP_PERIOD => Some(BK::NumpadDecimal),
251        GK::KP_ENTER => Some(BK::NumpadEnter),
252
253        // Additional common keys
254        GK::DELETE => Some(BK::Delete),
255        GK::INSERT => Some(BK::Insert),
256        GK::HOME => Some(BK::Home),
257        GK::END => Some(BK::End),
258        GK::PAGEUP => Some(BK::PageUp),
259        GK::PAGEDOWN => Some(BK::PageDown),
260        GK::CAPSLOCK => Some(BK::CapsLock),
261        GK::NUMLOCK => Some(BK::NumLock),
262        GK::SCROLLLOCK => Some(BK::ScrollLock),
263        GK::PAUSE => Some(BK::Pause),
264        GK::PRINT => Some(BK::PrintScreen),
265
266        // Punctuation and symbols
267        GK::COMMA => Some(BK::Comma),
268        GK::PERIOD => Some(BK::Period),
269        GK::SLASH => Some(BK::Slash),
270        GK::SEMICOLON => Some(BK::Semicolon),
271        GK::APOSTROPHE => Some(BK::Quote),
272        GK::BRACKETLEFT => Some(BK::BracketLeft),
273        GK::BRACKETRIGHT => Some(BK::BracketRight),
274        GK::BACKSLASH => Some(BK::Backslash),
275        GK::QUOTELEFT => Some(BK::Backquote),
276        GK::MINUS => Some(BK::Minus),
277        GK::EQUAL => Some(BK::Equal),
278
279        _ => None, // Many keys don't have direct equivalents
280    }
281}
282
283fn godot_mouse_to_bevy_mouse(godot_button: GodotMouseButton) -> BevyMouseButton {
284    match godot_button {
285        GodotMouseButton::Left => BevyMouseButton::Left,
286        GodotMouseButton::Right => BevyMouseButton::Right,
287        GodotMouseButton::Middle => BevyMouseButton::Middle,
288        GodotMouseButton::Extra1 => BevyMouseButton::Back,
289        GodotMouseButton::Extra2 => BevyMouseButton::Forward,
290        // Note: Bevy doesn't have wheel events as buttons
291        _ => BevyMouseButton::Other(255),
292    }
293}