Skip to content

Commit

Permalink
Merge pull request #8 from 17cupsofcoffee/sdl3
Browse files Browse the repository at this point in the history
Port to SDL3
  • Loading branch information
17cupsofcoffee authored Dec 6, 2024
2 parents eb53253 + a44ab86 commit c92d2ca
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 160 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ edition = "2021"

[dependencies]
# Core
fermium = { version = "22605", default-features = false }
sdl3-sys = { version = "0.1.3" }
glow = "0.16"
bytemuck = { version = "1.20", features = ["derive"] }
glam = { version = "0.29", features = ["bytemuck"] }
Expand All @@ -24,5 +24,5 @@ rand = "0.8"
[features]
default = ["ldtk"]
ldtk = ["serde", "serde_json"]
static_bundled_build = ["fermium/static_bundled_build"]
static_bundled_build = ["sdl3-sys/build-from-source-static"] # TODO: Probably split this up
serde = ["dep:serde", "glam/serde"]
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ on between 2018 and 2022. It aims to be smaller and simpler, with less global st
## Features

- `ldtk` (enabled by default): enables a module to load [ldtk](https://ldtk.io/) files.
- `static_bundled_build`: enables automatic SDL2 library building and linking. Building SDL2 can take a bit during that first build (usually 1 minute or more).
- `static_bundled_build`: enables automatic SDL3 library building and linking. Building SDL3 can take a bit during that first build (usually 1 minute or more).

## Notes

- This framework is very heavily inspired by [FNA](https://github.com/FNA-XNA/FNA), and NoelFB's lightweight game engines ([Blah](https://github.com/NoelFB/blah) and [Foster](https://github.com/NoelFB/Foster)).
- It depends on [SDL2](https://www.libsdl.org/) for interacting with the underlying platform.
- It depends on [SDL3](https://www.libsdl.org/) for interacting with the underlying platform.
9 changes: 6 additions & 3 deletions examples/bunnymark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use nova::app::{App, EventHandler};
use nova::graphics::{Batcher, Color, DrawParams, Texture};
use nova::input::{Key, MouseButton};
use nova::input::{GamepadButton, Key, MouseButton};
use nova::math::Vec2;
use rand::rngs::ThreadRng;
use rand::{self, Rng};
Expand Down Expand Up @@ -83,8 +83,11 @@ impl EventHandler for GameState {
self.auto_spawn = !self.auto_spawn;
}

let should_spawn = self.spawn_timer == 0
&& (app.input.is_mouse_button_down(MouseButton::Left) || self.auto_spawn);
let button_down = app.input.is_key_down(Key::Space)
|| app.input.is_mouse_button_down(MouseButton::Left)
|| app.input.is_gamepad_button_down(0, GamepadButton::A);

let should_spawn = self.spawn_timer == 0 && (button_down || self.auto_spawn);

if should_spawn {
for _ in 0..INITIAL_BUNNIES {
Expand Down
14 changes: 4 additions & 10 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use fermium::prelude::SDL_QUIT;

use crate::graphics::Graphics;
use crate::input::{Event, Input};
use crate::time::Timer;
Expand Down Expand Up @@ -69,17 +67,13 @@ impl App {

pub fn handle_events(&mut self, event_handler: &mut impl EventHandler) {
while let Some(event) = self.window.next_event() {
unsafe {
if event.type_ == SDL_QUIT {
self.is_running = false;
}
if let Event::Quit = event {
self.is_running = false;
}

if let Some(event) = crate::input::Event::try_from_sdl_event(&event) {
self.input.event(&event);
self.input.event(&event);

event_handler.event(self, event);
}
event_handler.event(self, event);
}
}
}
8 changes: 7 additions & 1 deletion src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ impl Input {
Event::MouseButtonDown(button) => self.mouse_buttons.set_down(*button),
Event::MouseButtonUp(button) => self.mouse_buttons.set_up(*button),
Event::MouseMotion { new_position } => self.mouse_position = *new_position,

Event::ControllerDeviceAdded { joystick, gamepad } => {
let empty_slot = self.gamepads.iter().position(Option::is_none);

Expand All @@ -63,21 +64,25 @@ impl Input {

self.joystick_ids.insert(*joystick, gamepad_id);
}

Event::ControllerDeviceRemoved { joystick } => {
if let Some(gamepad_id) = self.joystick_ids.remove(joystick) {
self.gamepads[gamepad_id] = None;
}
}

Event::ControllerButtonDown { joystick, button } => {
if let Some(gamepad_id) = self.joystick_ids.get(joystick) {
self.gamepad_buttons.set_down((*gamepad_id, *button));
}
}

Event::ControllerButtonUp { joystick, button } => {
if let Some(gamepad_id) = self.joystick_ids.get(joystick) {
self.gamepad_buttons.set_up((*gamepad_id, *button));
}
}

Event::ControllerAxisMotion {
joystick,
axis,
Expand All @@ -87,7 +92,8 @@ impl Input {
self.axes.set_value(*gamepad_id, *axis, *value);
}
}
Event::WindowResized { .. } | Event::TextInput { .. } => {}

_ => {}
}
}

Expand Down
113 changes: 51 additions & 62 deletions src/input/event.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,26 @@
use fermium::prelude::*;
use glam::Vec2;
use sdl3_sys::events::*;
use sdl3_sys::gamepad::*;

/// This is a unique ID for a joystick for the time it is connected to the
/// system.
///
/// It is never reused for the lifetime of the application. If the joystick is
/// disconnected and reconnected, it will get a new ID.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct JoystickID(SDL_JoystickID);

impl JoystickID {
fn from_raw(id: i32) -> JoystickID {
JoystickID(SDL_JoystickID(id))
}
fn from_controller_handle(handle: *mut SDL_GameController) -> JoystickID {
unsafe {
JoystickID(SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(
handle,
)))
}
}
}

use super::{Gamepad, GamepadAxis, GamepadButton, Key, MouseButton};
use super::{Gamepad, GamepadAxis, GamepadButton, JoystickID, Key, MouseButton};

#[derive(Debug, Clone, PartialEq)]
pub enum Event {
Quit,
KeyDown(Key),
KeyUp(Key),
MouseButtonDown(MouseButton),
MouseButtonUp(MouseButton),

MouseMotion {
new_position: Vec2,
},

ControllerDeviceAdded {
joystick: JoystickID,
gamepad: Gamepad,
},

ControllerDeviceRemoved {
joystick: JoystickID,
},
Expand All @@ -46,6 +29,7 @@ pub enum Event {
joystick: JoystickID,
button: GamepadButton,
},

ControllerButtonUp {
joystick: JoystickID,
button: GamepadButton,
Expand All @@ -56,6 +40,7 @@ pub enum Event {
axis: GamepadAxis,
value: f32,
},

WindowResized {
width: u32,
height: u32,
Expand All @@ -67,105 +52,108 @@ pub enum Event {
}

impl Event {
pub fn try_from_sdl_event(event: &SDL_Event) -> Option<Self> {
pub fn from_raw(event: &SDL_Event) -> Option<Event> {
unsafe {
match event.type_ {
SDL_KEYDOWN if event.key.repeat == 0 => {
if let Some(key) = Key::from_raw(event.key.keysym.scancode) {
match SDL_EventType(event.r#type) {
SDL_EVENT_QUIT => {
return Some(Event::Quit);
}

SDL_EVENT_KEY_DOWN if !event.key.repeat => {
if let Some(key) = Key::from_raw(event.key.scancode) {
return Some(Event::KeyDown(key));
}
}

SDL_KEYUP if event.key.repeat == 0 => {
if let Some(key) = Key::from_raw(event.key.keysym.scancode) {
SDL_EVENT_KEY_UP if !event.key.repeat => {
if let Some(key) = Key::from_raw(event.key.scancode) {
return Some(Event::KeyUp(key));
}
}

SDL_MOUSEBUTTONDOWN => {
if let Some(button) = MouseButton::from_raw(event.button.button as u32) {
SDL_EVENT_MOUSE_BUTTON_DOWN => {
if let Some(button) = MouseButton::from_raw(event.button.button as i32) {
return Some(Event::MouseButtonDown(button));
}
}

SDL_MOUSEBUTTONUP => {
if let Some(button) = MouseButton::from_raw(event.button.button as u32) {
SDL_EVENT_MOUSE_BUTTON_UP => {
if let Some(button) = MouseButton::from_raw(event.button.button as i32) {
return Some(Event::MouseButtonUp(button));
}
}

SDL_MOUSEMOTION => {
SDL_EVENT_MOUSE_MOTION => {
return Some(Event::MouseMotion {
new_position: Vec2::new(event.motion.x as f32, event.motion.y as f32),
new_position: Vec2::new(event.motion.x, event.motion.y),
});
}

SDL_CONTROLLERDEVICEADDED => {
let handle = SDL_GameControllerOpen(event.cdevice.which);
SDL_EVENT_GAMEPAD_ADDED => {
let handle = SDL_OpenGamepad(event.gdevice.which);

if handle.is_null() {
// TODO: Should probably log here
return None;
}

let joystick = JoystickID::from_controller_handle(handle);

let joystick = JoystickID::from_raw(event.gdevice.which);
let gamepad = Gamepad::from_raw(handle);

return Some(Event::ControllerDeviceAdded { joystick, gamepad });
}

SDL_CONTROLLERDEVICEREMOVED => {
SDL_EVENT_GAMEPAD_REMOVED => {
return Some(Event::ControllerDeviceRemoved {
joystick: JoystickID::from_raw(event.cdevice.which),
joystick: JoystickID::from_raw(event.gdevice.which),
});
}

SDL_CONTROLLERBUTTONDOWN => {
if let Some(button) = GamepadButton::from_raw(SDL_GameControllerButton(
event.cbutton.button as i32,
)) {
SDL_EVENT_GAMEPAD_BUTTON_DOWN => {
if let Some(button) =
GamepadButton::from_raw(SDL_GamepadButton(event.gbutton.button as i32))
{
return Some(Event::ControllerButtonDown {
joystick: JoystickID::from_raw(event.cdevice.which),
joystick: JoystickID::from_raw(event.gdevice.which),
button,
});
}
}

SDL_CONTROLLERBUTTONUP => {
if let Some(button) = GamepadButton::from_raw(SDL_GameControllerButton(
event.cbutton.button as i32,
)) {
SDL_EVENT_GAMEPAD_BUTTON_UP => {
if let Some(button) =
GamepadButton::from_raw(SDL_GamepadButton(event.gbutton.button as i32))
{
return Some(Event::ControllerButtonUp {
joystick: JoystickID::from_raw(event.cdevice.which),
joystick: JoystickID::from_raw(event.gdevice.which),
button,
});
}
}

SDL_CONTROLLERAXISMOTION => {
SDL_EVENT_GAMEPAD_AXIS_MOTION => {
if let Some(axis) =
GamepadAxis::from_raw(SDL_GameControllerAxis(event.caxis.axis as i32))
GamepadAxis::from_raw(SDL_GamepadAxis(event.gaxis.axis as i32))
{
let mut value = if event.caxis.value > 0 {
event.caxis.value as f32 / 32767.0
let mut value = if event.gaxis.value > 0 {
event.gaxis.value as f32 / 32767.0
} else {
event.caxis.value as f32 / 32768.0
event.gaxis.value as f32 / 32768.0
};

// TODO: Add less hacky deadzone logic
if value.abs() < 0.2 {
value = 0.0;
}
return Some(Event::ControllerAxisMotion {
joystick: JoystickID::from_raw(event.cdevice.which),
joystick: JoystickID::from_raw(event.gdevice.which),
axis,
value,
});
}
}

SDL_WINDOWEVENT => {
SDL_EVENT_WINDOW_RESIZED => {
let e = &event.window;
if e.data1 > 0 && e.data2 > 0 {
let width = e.data1 as u32;
Expand All @@ -174,17 +162,18 @@ impl Event {
}
}

SDL_TEXTINPUT => {
let e = &event.text.text;
let text = std::ffi::CStr::from_ptr(e.as_ptr())
SDL_EVENT_TEXT_INPUT => {
let text = std::ffi::CStr::from_ptr(event.text.text)
.to_string_lossy()
.into_owned();

return Some(Event::TextInput { text });
}

_ => {}
}
}

None
}
}
Loading

0 comments on commit c92d2ca

Please sign in to comment.