From 81bac10cd7c26cf1ca1c0fcb9ee3dcccd7710f22 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 9 Dec 2021 16:46:37 +0100 Subject: Initial gamecontroller support --- cmd/demo/main.ha | 189 +++++++++++++++++++++++++++++++++---------------- sdl2/events.ha | 2 +- sdl2/gamecontroller.ha | 43 +++++++++++ sdl2/joystick.ha | 13 ++++ 4 files changed, 184 insertions(+), 63 deletions(-) create mode 100644 sdl2/gamecontroller.ha create mode 100644 sdl2/joystick.ha diff --git a/cmd/demo/main.ha b/cmd/demo/main.ha index 43aea33..77a24a9 100644 --- a/cmd/demo/main.ha +++ b/cmd/demo/main.ha @@ -1,6 +1,11 @@ use fmt; use os; -use sdl2::{event_type, window_flags, renderer_flags}; +use sdl2::{ + controller_axis, + event_type, + renderer_flags, + window_flags, +}; use sdl2; use sdl2::image; use strings; @@ -12,6 +17,7 @@ type texture = struct { }; type state = struct { + quit: bool, window: *sdl2::window, render: *sdl2::renderer, hare: texture, @@ -22,10 +28,112 @@ type state = struct { hare_dy: int, cat_x: int, cat_y: int, + cat_dx: int, + cat_dy: int, +}; + +fn update(state: *state) void = { + let ev = sdl2::event { ... }; + for (sdl2::poll_event(&ev) == 1) switch (ev.event_type) { + case event_type::QUIT => + state.quit = true; + return; + case event_type::CONTROLLERAXISMOTION => + let delta = ev.caxis.value: int * 10 / sdl2::JOYSTICK_AXIS_MAX; + if (axis_x(ev.caxis.axis)) { + state.cat_dx = delta; + }; + if (axis_y(ev.caxis.axis)) { + state.cat_dy = delta; + }; + case => void; + }; + + let width = 0, height = 0; + sdl2::get_window_size(state.window, &width, &height); + + state.hare_x += state.hare_dx; + state.hare_y += state.hare_dy; + if (state.hare_x <= 0 || state.hare_x + state.hare.w >= width) { + state.hare_dx = -state.hare_dx; + }; + if (state.hare_y <= 0 || state.hare_y + state.hare.h >= height) { + state.hare_dy = -state.hare_dy; + }; + + state.cat_x += state.cat_dx; + state.cat_y += state.cat_dy; + if (state.cat_x <= 0) { + state.cat_x = 0; + }; + if (state.cat_x > width - state.cat.w) { + state.cat_x = width - state.cat.w; + }; + if (state.cat_y <= 0) { + state.cat_y = 0; + }; + if (state.cat_y > height - state.cat.h) { + state.cat_y = height - state.cat.h; + }; +}; + +fn draw(state: *state) void = { + sdl2::set_render_draw_color(state.render, 50, 50, 50, 255); + sdl2::render_clear(state.render); + draw_tex(state, &state.hare, state.hare_x, state.hare_y); + draw_tex(state, &state.cat, state.cat_x, state.cat_y); + sdl2::render_present(state.render); +}; + +fn draw_tex(state: *state, tex: *texture, x: int, y: int) void = { + sdl2::render_copy(state.render, tex.tex, null, &sdl2::rect { + x = x, + y = y, + w = tex.w, + h = tex.h, + }); +}; + +fn axis_x(axis: controller_axis) bool = { + switch (axis) { + case controller_axis::LEFTX, controller_axis::RIGHTX => + return true; + case => + return false; + }; +}; + +fn axis_y(axis: controller_axis) bool = { + switch (axis) { + case controller_axis::LEFTY, controller_axis::RIGHTY => + return true; + case => + return false; + }; +}; + +fn load_texture(render: *sdl2::renderer, path: str) texture = { + const path = strings::to_c(path); + defer free(path); + + const tex = match (image::load_texture(render, path)) { + case let tex: *sdl2::texture => + yield tex; + case null => + fmt::fatal("sdl2::image::load_texture failed for cat.png"); + }; + + let w = 0, h = 0; + sdl2::query_texture(tex, null, null, &w, &h); + return texture { + tex = tex, + w = w, + h = h, + }; }; export fn main() void = { - sdl2::init(sdl2::init_flags::VIDEO); + sdl2::init(sdl2::init_flags::VIDEO | sdl2::init_flags::GAMECONTROLLER); defer sdl2::quit(); let flags = image::init(image::init_flags::PNG | image::init_flags::JPG); defer image::quit(); @@ -50,6 +158,22 @@ export fn main() void = { }; defer sdl2::destroy_renderer(render); + let controller: nullable *sdl2::gamecontroller = null; + for (let i = 0; i < sdl2::numjoysticks(); i += 1) { + if (!sdl2::is_game_controller(i)) { + continue; + }; + controller = sdl2::game_controller_open(i); + if (controller != null) { + break; + }; + }; + defer match (controller) { + case null => void; + case let c: *sdl2::gamecontroller => + sdl2::game_controller_close(c); + }; + let state = state { window = win, render = render, @@ -64,70 +188,11 @@ export fn main() void = { defer sdl2::destroy_texture(state.hare.tex); defer sdl2::destroy_texture(state.cat.tex); - let ev = sdl2::event { ... }; - for (true) { - if (sdl2::poll_event(&ev) == 1) switch (ev.event_type) { - case event_type::QUIT => - break; - case => void; - }; - + for (!state.quit) { update(&state); draw(&state); - sdl2::delay(1000 / 60); }; os::exit(0); // https://todo.sr.ht/~sircmpwn/hare/525 }; - -fn load_texture(render: *sdl2::renderer, path: str) texture = { - const path = strings::to_c(path); - defer free(path); - - const tex = match (image::load_texture(render, path)) { - case let tex: *sdl2::texture => - yield tex; - case null => - fmt::fatal("sdl2::image::load_texture failed for cat.png"); - }; - - let w = 0, h = 0; - sdl2::query_texture(tex, null, null, &w, &h); - return texture { - tex = tex, - w = w, - h = h, - }; -}; - -fn update(state: *state) void = { - let width = 0, height = 0; - sdl2::get_window_size(state.window, &width, &height); - - state.hare_x += state.hare_dx; - state.hare_y += state.hare_dy; - if (state.hare_x <= 0 || state.hare_x + state.hare.w >= width) { - state.hare_dx = -state.hare_dx; - }; - if (state.hare_y <= 0 || state.hare_y + state.hare.h >= height) { - state.hare_dy = -state.hare_dy; - }; -}; - -fn draw(state: *state) void = { - sdl2::set_render_draw_color(state.render, 50, 50, 50, 255); - sdl2::render_clear(state.render); - draw_tex(state, &state.hare, state.hare_x, state.hare_y); - draw_tex(state, &state.cat, state.cat_x, state.cat_y); - sdl2::render_present(state.render); -}; - -fn draw_tex(state: *state, tex: *texture, x: int, y: int) void = { - sdl2::render_copy(state.render, tex.tex, null, &sdl2::rect { - x = x, - y = y, - w = tex.w, - h = tex.h, - }); -}; diff --git a/sdl2/events.ha b/sdl2/events.ha index 60ac771..e26333a 100644 --- a/sdl2/events.ha +++ b/sdl2/events.ha @@ -220,7 +220,7 @@ export type joy_device_event = struct { export type controller_axis_event = struct { common_event, which: i32, // TODO - axis: u8, + axis: controller_axis, padding1: u8, padding2: u8, padding3: u8, diff --git a/sdl2/gamecontroller.ha b/sdl2/gamecontroller.ha new file mode 100644 index 0000000..3b752e1 --- /dev/null +++ b/sdl2/gamecontroller.ha @@ -0,0 +1,43 @@ +// TODO: Flesh me out + +// The gamecontroller structure used to identify an SDL game controller. +// (Opaque) +export type gamecontroller = void; + +// The list of axes available from a controller +// +// Thumbstick axis values range from [[JOYSTICK_AXIS_MIN]] to +// [[JOYSTICK_AXIS_MAX]], and are centered within ~8000 of zero, though advanced +// UI will allow users to set or autodetect the dead zone, which varies between +// controllers. +// +// Trigger axis values range from 0 to [[JOYSTICK_AXIS_MAX]]. +export type controller_axis = enum u8 { + LEFTX, + LEFTY, + RIGHTX, + RIGHTY, + TRIGGERLEFT, + TRIGGERRIGHT, + INVALID = 255, +}; + +// Check if the given joystick is supported by the game controller interface. +// +// 'joystick_index' is the same as the 'device_index' passed to +// [[joystick_open]]. +// +// Returns SDL_TRUE if the given joystick is supported by the game controller +// interface, SDL_FALSE if it isn't or it's an invalid index. +export @symbol("SDL_IsGameController") fn is_game_controller( + joystick_index: int) bool; + +// Get the SDL_GameController associated with an instance id. +// +// Returns a [[controller]] on success or NULL on failure. +export @symbol("SDL_GameControllerOpen") fn game_controller_open( + joystick_index: int) *gamecontroller; + +// Close a game controller previously opened with [[game_controller_open]]. +export @symbol("SDL_GameControllerClose") fn game_controller_close( + gamecontroller: *gamecontroller) void; diff --git a/sdl2/joystick.ha b/sdl2/joystick.ha new file mode 100644 index 0000000..c4f4c96 --- /dev/null +++ b/sdl2/joystick.ha @@ -0,0 +1,13 @@ +// TODO: Flesh me out + +// Minimum value for a joystick axis. +export def JOYSTICK_AXIS_MIN: i16 = -32768; + +// Minimum value for a joystick axis. +export def JOYSTICK_AXIS_MAX: i16 = 32767; + +// Count the number of joysticks attached to the system. +// +// Returns the number of attached joysticks on success or a negative error code +// on failure. +export @symbol("SDL_NumJoysticks") fn numjoysticks() int; -- cgit v1.2.3