470 lines
12 KiB
C++
470 lines
12 KiB
C++
/*************************************************************************/
|
|
/* 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 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_PRINT("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;
|
|
|
|
for (int i = 0; i < MAX_ABS; ++i) {
|
|
abs_map[i] = -1;
|
|
curr_axis[i] = 0;
|
|
}
|
|
}
|
|
|
|
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_PRINT("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_PRINT("allocating report descriptor");
|
|
}
|
|
if (rep->size <= 0) {
|
|
ERR_PRINT("input report descriptor has invalid length");
|
|
}
|
|
hdata = hid_start_parse(joy->repdesc, 1 << hid_input, rep->rid);
|
|
if (hdata == NULL) {
|
|
ERR_PRINT("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_PRINT("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_PRINT("no ID available to assign to joypad device");
|
|
return;
|
|
}
|
|
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));
|
|
|
|
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 = "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 */
|
|
}
|
|
|
|
float JoypadOpenBSD::axis_correct(int min, int max, int p_value) const {
|
|
// Convert to a value between -1.0 and 1.0f.
|
|
return 2.0f * (p_value - min) / (max - min) - 1.0f;
|
|
}
|
|
|
|
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;) {
|
|
(void)nbutton;
|
|
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];
|
|
(void)naxe;
|
|
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();
|
|
}
|
|
|
|
#endif
|