I updated the gamecontroller diff for our current version (3.4.4) and
added op@'s suggestions. This works with XBox {360,One} controllers.
Others so far not tested. You can test controller detection simply by
running `$ godot`. If the controller is detected, a line like this
should appear:
joypad 0: 6 axes 10 buttons 1 hats
To test, you can download a free demo of a Godot game from itch.io, for
example Oddventure or Satryn. The usual way to run those is:
$ godot --main-pack <game>.pck
Sometimes the package is embedded in a Linux ELF binary, but can still
be run, like:
$ godot --main-pack satryn.x86_64
In the game, at least the joystick, D-pad, and the first 2 buttons
should work. With a game like Haiki, it detects the "Start" button, too.
The mapping isn't fully solved and this will probably be an area to work
on in the future.
The diff is at the bottom. Godot-style license info is included, as the
goal is to get the ujoy backend upstreamed eventually.
comments? ok?
On Mon, Nov 01, 2021 at 09:24:27AM +0100, Omar Polo wrote:
> Thomas Frohwein <tfrohwein@fastmail.com> writes:
>
> > Hi,
> >
> > This diff adds gamecontroller support to Godot. The code is largely
> > based on sdl2 and fit into Godot's framework.
>
> Hello :)
>
> As you know, I can't really do a runtime test (my ps3 gamepad is not
> supported :/) but the least I can do is provide some feedbacks on the
> diff.
>
> > [...]
> >
> > feedback and ok's welcome. I think in terms of functionality this is
> > at an okay point to add to the port and hopefully more widespread use
> > will help to figure out the remaining bits.
>
> of course I'm OK with this! (but read below)
>
> > Ultimately I will be working probably with Omar on upstreaming both
> > sndio and gamecontroller support. It seems that upstream is deferring
> > such updates to the eventual 4.0 release which is still ways out.
>
> I forgot to tell you the other day, the way upstream works (if I've
> understood correctly) is to merge improvements in the main branch
> (i.e. the future godot 4.0) and then eventually backport the changes to
> 3.X. So there's still hope for the sndio patch :D
>
> Also, if you have some questions on the godot internal there's an
> instance of rocketchat where we can ask godot devs.
>
> I have some comments on the diff (but keep in mind that i'm not a c++
> guy, take everything with a grain of salt)
[...]
Index: Makefile
===================================================================
RCS file: /cvs/ports/games/godot/Makefile,v
retrieving revision 1.33
diff -u -p -r1.33 Makefile
--- Makefile 23 Mar 2022 21:19:10 -0000 1.33
+++ Makefile 15 Apr 2022 14:49:36 -0000
@@ -6,6 +6,7 @@ V = 3.4.4
GODOTSTEAM_V = g34-s152-gs311
DISTNAME = godot-${V}-stable
PKGNAME = godot-${V}
+REVISION = 0
CATEGORIES = games
HOMEPAGE = https://godotengine.org/
MAINTAINER = Omar Polo <op@omarpolo.com>
@@ -17,7 +18,7 @@ WANTLIB += ${COMPILER_LIBCXX} BulletColl
WANTLIB += LinearMath GL X11 Xau Xcursor Xdmcp Xext Xfixes Xi Xinerama Xrandr
WANTLIB += Xrender c enet execinfo freetype intl m mbedtls mbedcrypto
WANTLIB += mbedx509 mpcdec ogg opus opusfile png sndio steam_api theora
-WANTLIB += theoradec vorbis vorbisfile webp xcb z pcre2-32 vpx zstd
+WANTLIB += theoradec usbhid vorbis vorbisfile webp xcb z pcre2-32 vpx zstd
# C++14
COMPILER = base-clang ports-gcc
@@ -37,7 +38,7 @@ MODSCONS_FLAGS = CC="${CC}" \
CXX="${CXX}" \
CFLAGS="${CFLAGS}" \
CXXFLAGS="${CXXFLAGS} -Wno-deprecated-register" \
- LINKFLAGS="${LDFLAGS} -lintl -lmpcdec" \
+ LINKFLAGS="${LDFLAGS} -lintl -lmpcdec -lusbhid" \
builtin_bullet=no \
builtin_enet=no \
builtin_freetype=no \
@@ -84,6 +85,7 @@ LIB_DEPENDS = archivers/zstd \
security/polarssl
RUN_DEPENDS = devel/desktop-file-utils
+DEBUG_PACKAGES = ${BUILD_PACKAGES}
NO_TEST = Yes
DPB_PROPERTIES = parallel
@@ -96,7 +98,8 @@ WANTLIB += atomic
CFLAGS += -I${LOCALBASE}/include/goldberg_emulator
post-extract:
- cp -R ${FILESDIR}/sndio ${WRKDIST}/drivers
+ cp -R ${FILESDIR}/sndio ${WRKDIST}/drivers
+ cp ${FILESDIR}/ujoy/* ${WRKDIST}/platform/x11/
pre-configure:
${SUBST_CMD} ${WRKSRC}/drivers/unix/os_unix.cpp
Index: files/ujoy/joypad_openbsd.cpp
===================================================================
RCS file: files/ujoy/joypad_openbsd.cpp
diff -N files/ujoy/joypad_openbsd.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ files/ujoy/joypad_openbsd.cpp 15 Apr 2022 14:49:37 -0000
@@ -0,0 +1,483 @@
+/*************************************************************************/
+/* joypad_openbsd.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+//author: Thomas Frohwein <thfr@openbsd.org>
+#ifdef JOYDEV_ENABLED
+
+#include "joypad_openbsd.h"
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+extern "C" {
+ #include <dev/usb/usb.h>
+ #include <dev/usb/usbhid.h>
+ #include <usbhid.h>
+}
+
+#define ERR_PRINTS(x) printf("ERROR: %s\n", x)
+
+#define HUG_DPAD_UP 0x90
+#define HUG_DPAD_DOWN 0x91
+#define HUG_DPAD_RIGHT 0x92
+#define HUG_DPAD_LEFT 0x93
+
+#define HAT_CENTERED 0x00
+#define HAT_UP 0x01
+#define HAT_RIGHT 0x02
+#define HAT_DOWN 0x04
+#define HAT_LEFT 0x08
+#define HAT_RIGHTUP (HAT_RIGHT|HAT_UP)
+#define HAT_RIGHTDOWN (HAT_RIGHT|HAT_DOWN)
+#define HAT_LEFTUP (HAT_LEFT|HAT_UP)
+#define HAT_LEFTDOWN (HAT_LEFT|HAT_DOWN)
+
+#define REP_BUF_DATA(rep) ((rep)->buf->ucr_data)
+
+static struct
+{
+ int uhid_report;
+ hid_kind_t kind;
+ const char *name;
+} const repinfo[] = {
+ {UHID_INPUT_REPORT, hid_input, "input"},
+ {UHID_OUTPUT_REPORT, hid_output, "output"},
+ {UHID_FEATURE_REPORT, hid_feature, "feature"}
+};
+
+enum
+{
+ REPORT_INPUT,
+ REPORT_OUTPUT,
+ REPORT_FEATURE
+};
+
+enum
+{
+ JOYAXE_X,
+ JOYAXE_Y,
+ JOYAXE_Z,
+ JOYAXE_SLIDER,
+ JOYAXE_WHEEL,
+ JOYAXE_RX,
+ JOYAXE_RY,
+ JOYAXE_RZ,
+ JOYAXE_count
+};
+
+static int
+report_alloc(struct report *r, struct report_desc *rd, int repind)
+{
+ int len;
+ len = hid_report_size(rd, repinfo[repind].kind, r->rid);
+ if (len < 0) {
+ ERR_PRINTS("Negative HID report siz");
+ return ERR_PARAMETER_RANGE_ERROR;
+ }
+ r->size = len;
+
+ if (r->size > 0) {
+ r->buf = (usb_ctl_report *)malloc(sizeof(*r->buf) - sizeof(REP_BUF_DATA(r)) + r->size);
+ if (r->buf == NULL)
+ return ERR_OUT_OF_MEMORY;
+ } else {
+ r->buf = NULL;
+ }
+
+ r->status = report::SREPORT_CLEAN;
+ return OK;
+}
+
+static int
+usage_to_joyaxe(unsigned usage)
+{
+ switch (usage) {
+ case HUG_X:
+ return JOYAXE_X;
+ case HUG_Y:
+ return JOYAXE_Y;
+ case HUG_Z:
+ return JOYAXE_Z;
+ case HUG_SLIDER:
+ return JOYAXE_SLIDER;
+ case HUG_WHEEL:
+ return JOYAXE_WHEEL;
+ case HUG_RX:
+ return JOYAXE_RX;
+ case HUG_RY:
+ return JOYAXE_RY;
+ case HUG_RZ:
+ return JOYAXE_RZ;
+ default:
+ return -1;
+ }
+}
+
+static unsigned
+hatval_conversion(int hatval)
+{
+ static const unsigned hat_dir_map[8] = {
+ HAT_UP, HAT_RIGHTUP, HAT_RIGHT, HAT_RIGHTDOWN,
+ HAT_DOWN, HAT_LEFTDOWN, HAT_LEFT, HAT_LEFTUP
+ };
+ if ((hatval & 7) == hatval)
+ return hat_dir_map[hatval];
+ else
+ return HAT_CENTERED;
+}
+
+/* calculate the value from the state of the dpad */
+int
+dpad_conversion(int *dpad)
+{
+ if (dpad[2]) {
+ if (dpad[0])
+ return HAT_RIGHTUP;
+ else if (dpad[1])
+ return HAT_RIGHTDOWN;
+ else
+ return HAT_RIGHT;
+ } else if (dpad[3]) {
+ if (dpad[0])
+ return HAT_LEFTUP;
+ else if (dpad[1])
+ return HAT_LEFTDOWN;
+ else
+ return HAT_LEFT;
+ } else if (dpad[0]) {
+ return HAT_UP;
+ } else if (dpad[1]) {
+ return HAT_DOWN;
+ }
+ return HAT_CENTERED;
+}
+
+JoypadOpenBSD::Joypad::Joypad() {
+ fd = -1;
+ dpad = 0;
+ devpath = "";
+}
+
+JoypadOpenBSD::Joypad::~Joypad() {
+}
+
+void JoypadOpenBSD::Joypad::reset() {
+ dpad = 0;
+ fd = -1;
+
+ InputDefault::JoyAxis jx;
+ jx.min = -1;
+ jx.value = 0.0f;
+}
+
+JoypadOpenBSD::JoypadOpenBSD(InputDefault *in) {
+ input = in;
+ joy_thread.start(joy_thread_func, this);
+ hid_init(NULL);
+}
+
+JoypadOpenBSD::~JoypadOpenBSD() {
+ exit_monitor.set();
+ joy_thread.wait_to_finish();
+ close_joypad();
+}
+
+void JoypadOpenBSD::joy_thread_func(void *p_user) {
+ if (p_user) {
+ JoypadOpenBSD *joy = (JoypadOpenBSD *)p_user;
+ joy->run_joypad_thread();
+ }
+}
+
+void JoypadOpenBSD::run_joypad_thread() {
+ monitor_joypads();
+}
+
+void JoypadOpenBSD::monitor_joypads() {
+ while (!exit_monitor.is_set()) {
+ joy_mutex.lock();
+ for (int i = 0; i < 32; i++) {
+ char fname[64];
+ sprintf(fname, "/dev/ujoy/%d", i);
+ if (attached_devices.find(fname) == -1) {
+ open_joypad(fname);
+ }
+ }
+ joy_mutex.unlock();
+ usleep(1000000); // 1s
+ }
+}
+
+int JoypadOpenBSD::get_joy_from_path(String p_path) const {
+ for (int i = 0; i < JOYPADS_MAX; i++) {
+ if (joypads[i].devpath == p_path) {
+ return i;
+ }
+ }
+ return -2;
+}
+
+void JoypadOpenBSD::close_joypad(int p_id) {
+ if (p_id == -1) {
+ for (int i = 0; i < JOYPADS_MAX; i++) {
+ close_joypad(i);
+ };
+ return;
+ } else if (p_id < 0)
+ return;
+
+ Joypad &joy = joypads[p_id];
+
+ if (joy.fd != -1) {
+ close(joy.fd);
+ joy.fd = -1;
+ attached_devices.remove(attached_devices.find(joy.devpath));
+ input->joy_connection_changed(p_id, false, "");
+ };
+}
+
+void JoypadOpenBSD::setup_joypad_properties(int p_id) {
+ Error err;
+ Joypad *joy = &joypads[p_id];
+ struct hid_item hitem;
+ struct hid_data *hdata;
+ struct report *rep = NULL;
+ int i;
+ int ax;
+
+ for (ax = 0; ax < JOYAXE_count; ax++)
+ joy->axis_map[ax] = -1;
+
+ joy->type = Joypad::BSDJOY_UHID; // TODO: hardcoded; later check if /dev/joyX or /dev/ujoy/X
+ joy->repdesc = hid_get_report_desc(joy->fd);
+ if (joy->repdesc == NULL) {
+ ERR_PRINTS("getting USB report descriptor");
+ }
+ rep = &joy->inreport;
+ if (ioctl(joy->fd, USB_GET_REPORT_ID, &rep->rid) < 0) {
+ rep->rid = -1; /* XXX */
+ }
+ err = (Error)report_alloc(rep, joy->repdesc, REPORT_INPUT);
+ if (err != OK) {
+ ERR_PRINTS("allocating report descriptor");
+ }
+ if (rep->size <= 0) {
+ ERR_PRINTS("input report descriptor has invalid length");
+ }
+ hdata = hid_start_parse(joy->repdesc, 1 << hid_input, rep->rid);
+ if (hdata == NULL) {
+ ERR_PRINTS("cannot start HID parser");
+ }
+
+ int num_buttons = 0;
+ int num_axes = 0;
+ int num_hats = 0;
+
+ while (hid_get_item(hdata, &hitem)) {
+ switch (hitem.kind) {
+ case hid_input:
+ switch (HID_PAGE(hitem.usage)) {
+ case HUP_GENERIC_DESKTOP: {
+ unsigned usage = HID_USAGE(hitem.usage);
+ int joyaxe = usage_to_joyaxe(usage);
+ if (joyaxe >= 0) {
+ joy->axis_map[joyaxe] = 1;
+ } else if (usage == HUG_HAT_SWITCH || usage == HUG_DPAD_UP) {
+ num_hats++;
+ }
+ break;
+ }
+ case HUP_BUTTON:
+ num_buttons++;
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ hid_end_parse(hdata);
+ for (i = 0; i < JOYAXE_count; i++)
+ if (joy->axis_map[i] > 0)
+ joy->axis_map[i] = num_axes++;
+
+ if (num_axes == 0 && num_buttons == 0 && num_hats == 0) {
+ ERR_PRINTS("Not a joystick!");
+ } else {
+ printf("joypad %d: %d axes %d buttons %d hats\n", p_id, num_axes, num_buttons, num_hats);
+ }
+ joy->force_feedback = false;
+ joy->ff_effect_timestamp = 0;
+}
+
+void JoypadOpenBSD::open_joypad(const char *p_path) {
+ int joy_num = input->get_unused_joy_id();
+ if (joy_num < 0) {
+ ERR_PRINTS("no ID available to assign to joypad device");
+ }
+ int fd = open(p_path, O_RDONLY | O_NONBLOCK);
+ if (fd != -1 && joy_num != -1) {
+ // add to attached devices so we don't try to open it again
+ attached_devices.push_back(String(p_path));
+
+ char uid[128];
+ String name = "";
+
+ joypads[joy_num].reset();
+
+ Joypad &joy = joypads[joy_num];
+ joy.fd = fd;
+ joy.devpath = String(p_path);
+ setup_joypad_properties(joy_num);
+ String uidname = uid;
+ uidname += "00";
+ input->joy_connection_changed(joy_num, true, name, uidname);
+ }
+}
+
+void JoypadOpenBSD::joypad_vibration_start(int p_id, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
+ /* not supported */
+}
+
+void JoypadOpenBSD::joypad_vibration_stop(int p_id, uint64_t p_timestamp) {
+ /* not supported */
+}
+
+InputDefault::JoyAxis JoypadOpenBSD::axis_correct(int min, int max, int p_value) const {
+ InputDefault::JoyAxis jx;
+
+ if (min < 0) {
+ jx.min = -1;
+ if (p_value < 0) {
+ jx.value = (float)-p_value / min;
+ } else {
+ jx.value = (float)p_value / max;
+ }
+ } else if (min == 0) {
+ jx.min = 0;
+ jx.value = 0.0f + (float)p_value / max;
+ }
+ return jx;
+}
+
+void JoypadOpenBSD::process_joypads() {
+ struct hid_item hitem;
+ struct hid_data *hdata;
+ struct report *rep;
+ int nbutton, naxe = -1;
+ int v;
+ int dpad[4] = { 0, 0, 0, 0 };
+ int actualbutton;
+
+ if (joy_mutex.try_lock() != OK) {
+ return;
+ }
+ for (int i = 0; i < JOYPADS_MAX; i++) {
+ if (joypads[i].fd == -1) continue;
+
+ Joypad *joy = &joypads[i];
+ rep = &joy->inreport;
+
+ while (true) {
+ ssize_t r = read(joy->fd, REP_BUF_DATA(rep), rep->size);
+ if (r < 0 || (size_t)r != rep->size) {
+ break;
+ }
+
+ hdata = hid_start_parse(joy->repdesc, 1 << hid_input, rep->rid);
+ if (hdata == NULL) {
+ continue;
+ }
+
+ for (nbutton = 0; hid_get_item(hdata, &hitem) > 0;) {
+ switch (hitem.kind) {
+ case hid_input:
+ unsigned usage;
+ int joyaxe;
+ switch (HID_PAGE(hitem.usage)) {
+ case HUP_GENERIC_DESKTOP:
+ usage = HID_USAGE(hitem.usage);
+ joyaxe = usage_to_joyaxe(usage);
+ if (joyaxe >= 0) {
+ naxe = joy->axis_map[joyaxe];
+ v = hid_get_data(REP_BUF_DATA(rep), &hitem);
+ /* XInput controllermapping relies on inverted Y axes.
+ * These devices have a 16bit signed space, as opposed
+ * to older DInput devices (8bit unsigned), so
+ * hitem.logical_maximum can be used to differentiate them.
+ */
+ if ((joyaxe == JOYAXE_Y || joyaxe == JOYAXE_RY)
+ && hitem.logical_maximum > 255) {
+ if (v != 0)
+ v = ~v;
+ }
+ input->joy_axis(i, joyaxe, axis_correct(hitem.logical_minimum, hitem.logical_maximum, v));
+ break;
+ } else if (usage == HUG_HAT_SWITCH) {
+ v = hid_get_data(REP_BUF_DATA(rep), &hitem);
+ joy->dpad = hatval_conversion(v);
+ } else if (usage == HUG_DPAD_UP) {
+ dpad[0] = hid_get_data(REP_BUF_DATA(rep), &hitem);
+ joy->dpad = dpad_conversion(dpad);
+ } else if (usage == HUG_DPAD_DOWN) {
+ dpad[1] = hid_get_data(REP_BUF_DATA(rep), &hitem);
+ joy->dpad = dpad_conversion(dpad);
+ } else if (usage == HUG_DPAD_RIGHT) {
+ dpad[2] = hid_get_data(REP_BUF_DATA(rep), &hitem);
+ joy->dpad = dpad_conversion(dpad);
+ } else if (usage == HUG_DPAD_LEFT) {
+ dpad[3] = hid_get_data(REP_BUF_DATA(rep), &hitem);
+ joy->dpad = dpad_conversion(dpad);
+ }
+ input->joy_hat(i, joy->dpad);
+ break;
+ case HUP_BUTTON:
+ v = hid_get_data(REP_BUF_DATA(rep), &hitem);
+ actualbutton = HID_USAGE(hitem.usage) - 1; // buttons are zero-based
+ input->joy_button(i, actualbutton, v);
+ default:
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ hid_end_parse(hdata);
+ }
+ }
+ joy_mutex.unlock();
+}
+
+
No comments:
Post a Comment