changeset 443:dfc65293d984

core: initial gamepad support
author David Demelier <markand@malikania.fr>
date Sat, 21 Jan 2023 20:20:34 +0100
parents 9c3b3935f0aa
children 9f062143e675
files GNUmakefile libmlk-core/mlk/core/event.c libmlk-core/mlk/core/event.h libmlk-core/mlk/core/gamepad.c libmlk-core/mlk/core/gamepad.h libmlk-core/mlk/core/sys.c
diffstat 6 files changed, 257 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/GNUmakefile	Thu Nov 03 21:09:25 2022 +0100
+++ b/GNUmakefile	Sat Jan 21 20:20:34 2023 +0100
@@ -193,6 +193,7 @@
                         libmlk-core/mlk/core/event.c \
                         libmlk-core/mlk/core/font.c \
                         libmlk-core/mlk/core/game.c \
+                        libmlk-core/mlk/core/gamepad.c \
                         libmlk-core/mlk/core/image.c \
                         libmlk-core/mlk/core/maths.c \
                         libmlk-core/mlk/core/music.c \
--- a/libmlk-core/mlk/core/event.c	Thu Nov 03 21:09:25 2022 +0100
+++ b/libmlk-core/mlk/core/event.c	Sat Jan 21 20:20:34 2023 +0100
@@ -175,6 +175,29 @@
 	{ -1,                   MOUSE_BUTTON_NONE       }
 };
 
+/* Maintain with enum mlk_gamepad_button in gamepad.h */
+static const struct {
+	int button;
+	enum mlk_gamepad_button value;
+} pads[] = {
+	{ SDL_CONTROLLER_BUTTON_A,              MLK_GAMEPAD_BUTTON_A            },
+	{ SDL_CONTROLLER_BUTTON_B,              MLK_GAMEPAD_BUTTON_B            },
+	{ SDL_CONTROLLER_BUTTON_X,              MLK_GAMEPAD_BUTTON_X            },
+	{ SDL_CONTROLLER_BUTTON_Y,              MLK_GAMEPAD_BUTTON_Y            },
+	{ SDL_CONTROLLER_BUTTON_BACK,           MLK_GAMEPAD_BUTTON_BACK         },
+	{ SDL_CONTROLLER_BUTTON_GUIDE,          MLK_GAMEPAD_BUTTON_LOGO         },
+	{ SDL_CONTROLLER_BUTTON_START,          MLK_GAMEPAD_BUTTON_START        },
+	{ SDL_CONTROLLER_BUTTON_LEFTSTICK,      MLK_GAMEPAD_BUTTON_LTHUMB       },
+	{ SDL_CONTROLLER_BUTTON_RIGHTSTICK,     MLK_GAMEPAD_BUTTON_RTHUMB       },
+	{ SDL_CONTROLLER_BUTTON_LEFTSHOULDER,   MLK_GAMEPAD_BUTTON_LSHOULDER    },
+	{ SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,  MLK_GAMEPAD_BUTTON_RSHOULDER    },
+	{ SDL_CONTROLLER_BUTTON_DPAD_UP,        MLK_GAMEPAD_BUTTON_UP           },
+	{ SDL_CONTROLLER_BUTTON_DPAD_DOWN,      MLK_GAMEPAD_BUTTON_DOWN         },
+	{ SDL_CONTROLLER_BUTTON_DPAD_LEFT,      MLK_GAMEPAD_BUTTON_LEFT         },
+	{ SDL_CONTROLLER_BUTTON_DPAD_RIGHT,     MLK_GAMEPAD_BUTTON_RIGHT        },
+	{ -1,                                   MLK_GAMEPAD_BUTTON_UNKNOWN      }
+};
+
 static void
 convert_key(const SDL_Event *event, union event *ev)
 {
@@ -222,11 +245,53 @@
 	}
 }
 
+static void
+convert_pad(const SDL_Event *event, union event *ev)
+{
+	ev->type = event->type == SDL_CONTROLLERBUTTONDOWN ? EVENT_PADDOWN : EVENT_PADUP;
+
+	for (size_t i = 0; pads[i].value != MLK_GAMEPAD_BUTTON_UNKNOWN; ++i) {
+		if (pads[i].button == event->cbutton.button) {
+			ev->pad.button = pads[i].value;
+			break;
+		}
+	}
+}
+
+static const struct {
+	int axis;
+	enum mlk_gamepad_axis value;
+} axises[] = {
+	{ SDL_CONTROLLER_AXIS_LEFTX,            MLK_GAMEPAD_AXIS_LX             },
+	{ SDL_CONTROLLER_AXIS_LEFTY,            MLK_GAMEPAD_AXIS_LY             },
+	{ SDL_CONTROLLER_AXIS_RIGHTX,           MLK_GAMEPAD_AXIS_RX             },
+	{ SDL_CONTROLLER_AXIS_RIGHTY,           MLK_GAMEPAD_AXIS_RY             },
+	{ SDL_CONTROLLER_AXIS_TRIGGERLEFT,      MLK_GAMEPAD_AXIS_LTRIGGER       },
+	{ SDL_CONTROLLER_AXIS_TRIGGERRIGHT,     MLK_GAMEPAD_AXIS_RTRIGGER       },
+	{ -1,                                   MLK_GAMEPAD_AXIS_UNKNOWN        }
+};
+
+static void
+convert_axis(const SDL_Event *event, union event *ev)
+{
+	ev->type = EVENT_AXIS;
+	ev->axis.value = event->caxis.value;
+
+	for (size_t i = 0; axises[i].value != MLK_GAMEPAD_AXIS_UNKNOWN; ++i) {
+		if (axises[i].axis == event->caxis.axis) {
+			ev->axis.axis = axises[i].value;
+			break;
+		}
+	}
+}
+
 int
 event_poll(union event *ev)
 {
 	SDL_Event event;
 
+	memset(ev, 0, sizeof (*ev));
+
 	/*
 	 * Loop until we find an event we want to report, we skip unneeded
 	 * ones.
@@ -247,6 +312,13 @@
 		case SDL_MOUSEBUTTONUP:
 			convert_click(&event, ev);
 			return 1;
+		case SDL_CONTROLLERBUTTONDOWN:
+		case SDL_CONTROLLERBUTTONUP:
+			convert_pad(&event, ev);
+			return 1;
+		case SDL_CONTROLLERAXISMOTION:
+			convert_axis(&event, ev);
+			return 1;
 		default:
 			continue;
 		}
--- a/libmlk-core/mlk/core/event.h	Thu Nov 03 21:09:25 2022 +0100
+++ b/libmlk-core/mlk/core/event.h	Sat Jan 21 20:20:34 2023 +0100
@@ -22,6 +22,7 @@
 #include "core.h"
 #include "key.h"
 #include "mouse.h"
+#include "gamepad.h"
 
 enum event_type {
 	EVENT_CLICKDOWN,
@@ -29,6 +30,9 @@
 	EVENT_KEYDOWN,
 	EVENT_KEYUP,
 	EVENT_MOUSE,
+	EVENT_PADUP,
+	EVENT_PADDOWN,
+	EVENT_AXIS,
 	EVENT_QUIT,
 	EVENT_NUM
 };
@@ -53,11 +57,24 @@
 	unsigned int clicks;
 };
 
+struct mlk_event_pad {
+	enum event_type type;
+	enum mlk_gamepad_button button;
+};
+
+struct mlk_event_axis {
+	enum event_type type;
+	enum mlk_gamepad_axis axis;
+	int value;
+};
+
 union event {
 	enum event_type type;
 	struct event_key key;
 	struct event_mouse mouse;
 	struct event_click click;
+	struct mlk_event_pad pad;
+	struct mlk_event_axis axis;
 };
 
 CORE_BEGIN_DECLS
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-core/mlk/core/gamepad.c	Sat Jan 21 20:20:34 2023 +0100
@@ -0,0 +1,86 @@
+/*
+ * gamepad.c -- game controller support
+ *
+ * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <SDL.h>
+
+#include "err.h"
+#include "gamepad.h"
+
+int
+mlk_gamepad_open(struct mlk_gamepad *pad, int idx)
+{
+	assert(pad);
+
+	memset(pad, 0, sizeof (*pad));
+
+	if (!(pad->handle = SDL_GameControllerOpen(idx)))
+		return MLK_ERR_SDL;
+
+	return 0;
+}
+
+void
+mlk_gamepad_finish(struct mlk_gamepad *pad)
+{
+	assert(pad);
+
+	if (pad->handle)
+		SDL_GameControllerClose(pad->handle);
+
+	memset(pad, 0, sizeof (*pad));
+}
+
+int
+mlk_gamepad_iter_begin(struct mlk_gamepad_iter *it)
+{
+	assert(it);
+
+	memset(it, 0, sizeof (*it));
+	it->idx = -1;
+
+	if ((it->end = SDL_NumJoysticks()) < 0) {
+		it->end = 0;
+		return MLK_ERR_SDL;
+	}
+
+	return 0;
+}
+
+int
+mlk_gamepad_iter_next(struct mlk_gamepad_iter *it)
+{
+	/*
+	 * Go to the next gamepad, we need to iterate because SDL can combines
+	 * joystick and game controllers with the same API.
+	 */
+	for (++it->idx; it->idx < it->end && !SDL_IsGameController(it->idx); ++it->idx)
+		continue;
+
+	/* End of iteration. */
+	if (it->idx >= it->end) {
+		memset(it, 0, sizeof (*it));
+		return 0;
+	}
+
+	it->name = SDL_GameControllerNameForIndex(it->idx);
+
+	return 1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-core/mlk/core/gamepad.h	Sat Jan 21 20:20:34 2023 +0100
@@ -0,0 +1,80 @@
+/*
+ * gamepad.h -- game controller support
+ *
+ * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MLK_CORE_GAMEPAD_H
+#define MLK_CORE_GAMEPAD_H
+
+#include "core.h"
+
+CORE_BEGIN_DECLS
+
+enum mlk_gamepad_button {
+	MLK_GAMEPAD_BUTTON_UNKNOWN,
+	MLK_GAMEPAD_BUTTON_A,
+	MLK_GAMEPAD_BUTTON_B,
+	MLK_GAMEPAD_BUTTON_X,
+	MLK_GAMEPAD_BUTTON_Y,
+	MLK_GAMEPAD_BUTTON_BACK,
+	MLK_GAMEPAD_BUTTON_LOGO,
+	MLK_GAMEPAD_BUTTON_START,
+	MLK_GAMEPAD_BUTTON_LTHUMB,
+	MLK_GAMEPAD_BUTTON_RTHUMB,
+	MLK_GAMEPAD_BUTTON_LSHOULDER,
+	MLK_GAMEPAD_BUTTON_RSHOULDER,
+	MLK_GAMEPAD_BUTTON_UP,
+	MLK_GAMEPAD_BUTTON_DOWN,
+	MLK_GAMEPAD_BUTTON_LEFT,
+	MLK_GAMEPAD_BUTTON_RIGHT
+};
+
+enum mlk_gamepad_axis {
+	MLK_GAMEPAD_AXIS_UNKNOWN,
+	MLK_GAMEPAD_AXIS_LX,
+	MLK_GAMEPAD_AXIS_LY,
+	MLK_GAMEPAD_AXIS_RX,
+	MLK_GAMEPAD_AXIS_RY,
+	MLK_GAMEPAD_AXIS_LTRIGGER,
+	MLK_GAMEPAD_AXIS_RTRIGGER
+};
+
+struct mlk_gamepad {
+	const char *name;
+	void *handle;
+};
+
+struct mlk_gamepad_iter {
+	const char *name;
+	int idx;
+	int end;
+};
+
+int
+mlk_gamepad_open(struct mlk_gamepad *pad, int idx);
+
+void
+mlk_gamepad_finish(struct mlk_gamepad *pad);
+
+int
+mlk_gamepad_iter_begin(struct mlk_gamepad_iter *);
+
+int
+mlk_gamepad_iter_next(struct mlk_gamepad_iter *);
+
+CORE_END_DECLS
+
+#endif /* !MLK_CORE_GAMEPAD_H */
--- a/libmlk-core/mlk/core/sys.c	Thu Nov 03 21:09:25 2022 +0100
+++ b/libmlk-core/mlk/core/sys.c	Sat Jan 21 20:20:34 2023 +0100
@@ -184,7 +184,7 @@
 	(void)name;
 
 	/* SDL2. */
-	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
+	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0)
 		return errorf("%s", SDL_GetError());
 	if (IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG)
 		return errorf("%s", SDL_GetError());