forked from mirrors/gecko-dev
Bug 1680982 - Implement Linux/Unix Gamepad support using evdev. r=stransky
Based on work by: Val Packett <val@packett.cool> and coolreader18 <coolreader18@gmail.com>. Differential Revision: https://phabricator.services.mozilla.com/D197865
This commit is contained in:
parent
e0c8b40945
commit
761ca4cf49
4 changed files with 269 additions and 108 deletions
|
|
@ -19,53 +19,12 @@
|
|||
|
||||
namespace mozilla::dom {
|
||||
|
||||
// Follow the canonical ordering recommendation for the "Standard Gamepad"
|
||||
// from https://www.w3.org/TR/gamepad/#remapping.
|
||||
enum CanonicalButtonIndex {
|
||||
BUTTON_INDEX_PRIMARY,
|
||||
BUTTON_INDEX_SECONDARY,
|
||||
BUTTON_INDEX_TERTIARY,
|
||||
BUTTON_INDEX_QUATERNARY,
|
||||
BUTTON_INDEX_LEFT_SHOULDER,
|
||||
BUTTON_INDEX_RIGHT_SHOULDER,
|
||||
BUTTON_INDEX_LEFT_TRIGGER,
|
||||
BUTTON_INDEX_RIGHT_TRIGGER,
|
||||
BUTTON_INDEX_BACK_SELECT,
|
||||
BUTTON_INDEX_START,
|
||||
BUTTON_INDEX_LEFT_THUMBSTICK,
|
||||
BUTTON_INDEX_RIGHT_THUMBSTICK,
|
||||
BUTTON_INDEX_DPAD_UP,
|
||||
BUTTON_INDEX_DPAD_DOWN,
|
||||
BUTTON_INDEX_DPAD_LEFT,
|
||||
BUTTON_INDEX_DPAD_RIGHT,
|
||||
BUTTON_INDEX_META,
|
||||
BUTTON_INDEX_COUNT
|
||||
};
|
||||
|
||||
enum CanonicalAxisIndex {
|
||||
AXIS_INDEX_LEFT_STICK_X,
|
||||
AXIS_INDEX_LEFT_STICK_Y,
|
||||
AXIS_INDEX_RIGHT_STICK_X,
|
||||
AXIS_INDEX_RIGHT_STICK_Y,
|
||||
AXIS_INDEX_COUNT
|
||||
};
|
||||
|
||||
const float BUTTON_THRESHOLD_VALUE = 0.1f;
|
||||
|
||||
float NormalizeTouch(long aValue, long aMin, long aMax) {
|
||||
return (2.f * (aValue - aMin) / static_cast<float>(aMax - aMin)) - 1.f;
|
||||
}
|
||||
|
||||
bool AxisNegativeAsButton(float input) {
|
||||
const float value = (input < -0.5f) ? 1.f : 0.f;
|
||||
return value > BUTTON_THRESHOLD_VALUE;
|
||||
}
|
||||
|
||||
bool AxisPositiveAsButton(float input) {
|
||||
const float value = (input > 0.5f) ? 1.f : 0.f;
|
||||
return value > BUTTON_THRESHOLD_VALUE;
|
||||
}
|
||||
|
||||
double AxisToButtonValue(double aValue) {
|
||||
// Mapping axis value range from (-1, +1) to (0, +1).
|
||||
return (aValue + 1.0f) * 0.5f;
|
||||
|
|
|
|||
|
|
@ -98,6 +98,41 @@ enum class GamepadId : uint32_t {
|
|||
kVendor2836Product0001 = 0x28360001,
|
||||
};
|
||||
|
||||
// Follow the canonical ordering recommendation for the "Standard Gamepad"
|
||||
// from https://www.w3.org/TR/gamepad/#remapping.
|
||||
enum CanonicalButtonIndex {
|
||||
BUTTON_INDEX_PRIMARY,
|
||||
BUTTON_INDEX_SECONDARY,
|
||||
BUTTON_INDEX_TERTIARY,
|
||||
BUTTON_INDEX_QUATERNARY,
|
||||
BUTTON_INDEX_LEFT_SHOULDER,
|
||||
BUTTON_INDEX_RIGHT_SHOULDER,
|
||||
BUTTON_INDEX_LEFT_TRIGGER,
|
||||
BUTTON_INDEX_RIGHT_TRIGGER,
|
||||
BUTTON_INDEX_BACK_SELECT,
|
||||
BUTTON_INDEX_START,
|
||||
BUTTON_INDEX_LEFT_THUMBSTICK,
|
||||
BUTTON_INDEX_RIGHT_THUMBSTICK,
|
||||
BUTTON_INDEX_DPAD_UP,
|
||||
BUTTON_INDEX_DPAD_DOWN,
|
||||
BUTTON_INDEX_DPAD_LEFT,
|
||||
BUTTON_INDEX_DPAD_RIGHT,
|
||||
BUTTON_INDEX_META,
|
||||
BUTTON_INDEX_COUNT
|
||||
};
|
||||
|
||||
enum CanonicalAxisIndex {
|
||||
AXIS_INDEX_LEFT_STICK_X,
|
||||
AXIS_INDEX_LEFT_STICK_Y,
|
||||
AXIS_INDEX_RIGHT_STICK_X,
|
||||
AXIS_INDEX_RIGHT_STICK_Y,
|
||||
AXIS_INDEX_COUNT
|
||||
};
|
||||
|
||||
static inline bool AxisNegativeAsButton(double input) { return input < -0.5; }
|
||||
|
||||
static inline bool AxisPositiveAsButton(double input) { return input > 0.5; }
|
||||
|
||||
class GamepadRemapper {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GamepadRemapper)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
* LinuxGamepadService: A Linux backend for the GamepadService.
|
||||
* Derived from the kernel documentation at
|
||||
* http://www.kernel.org/doc/Documentation/input/joystick-api.txt
|
||||
* LinuxGamepadService: An evdev backend for the GamepadService.
|
||||
*
|
||||
* Ref: https://www.kernel.org/doc/html/latest/input/gamepad.html
|
||||
*/
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <cstddef>
|
||||
|
||||
#include <glib.h>
|
||||
#include <linux/joystick.h>
|
||||
#include <linux/input.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
|
@ -21,10 +22,15 @@
|
|||
#include "nscore.h"
|
||||
#include "mozilla/dom/GamepadHandle.h"
|
||||
#include "mozilla/dom/GamepadPlatformService.h"
|
||||
#include "mozilla/dom/GamepadRemapping.h"
|
||||
#include "mozilla/Tainting.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "udev.h"
|
||||
|
||||
#define BITS_PER_LONG ((sizeof(unsigned long)) * 8)
|
||||
#define BITS_TO_LONGS(x) (((x) + BITS_PER_LONG - 1) / BITS_PER_LONG)
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
|
@ -36,19 +42,42 @@ using mozilla::udev_list_entry;
|
|||
using mozilla::udev_monitor;
|
||||
using mozilla::UniquePtr;
|
||||
|
||||
static const float kMaxAxisValue = 32767.0;
|
||||
static const char kJoystickPath[] = "/dev/input/js";
|
||||
static const char kEvdevPath[] = "/dev/input/event";
|
||||
|
||||
static inline bool TestBit(const unsigned long* arr, size_t bit) {
|
||||
return !!(arr[bit / BITS_PER_LONG] & (1LL << (bit % BITS_PER_LONG)));
|
||||
}
|
||||
|
||||
static inline double ScaleAxis(const input_absinfo& info, int value) {
|
||||
return 2.0 * (value - info.minimum) / (double)(info.maximum - info.minimum) -
|
||||
1.0;
|
||||
}
|
||||
|
||||
// TODO: should find a USB identifier for each device so we can
|
||||
// provide something that persists across connect/disconnect cycles.
|
||||
typedef struct {
|
||||
struct Gamepad {
|
||||
GamepadHandle handle;
|
||||
guint source_id;
|
||||
int numAxes;
|
||||
int numButtons;
|
||||
char idstring[256];
|
||||
char devpath[PATH_MAX];
|
||||
} Gamepad;
|
||||
bool isStandardGamepad = false;
|
||||
RefPtr<GamepadRemapper> remapper = nullptr;
|
||||
guint source_id = UINT_MAX;
|
||||
char idstring[256] = {0};
|
||||
char devpath[PATH_MAX] = {0};
|
||||
uint8_t key_map[KEY_MAX] = {0};
|
||||
uint8_t abs_map[ABS_MAX] = {0};
|
||||
std::unordered_map<uint16_t, input_absinfo> abs_info;
|
||||
};
|
||||
|
||||
static inline bool LoadAbsInfo(int fd, Gamepad* gamepad, uint16_t code) {
|
||||
input_absinfo info{0};
|
||||
if (ioctl(fd, EVIOCGABS(code), &info) < 0) {
|
||||
return false;
|
||||
}
|
||||
if (info.minimum == info.maximum) {
|
||||
return false;
|
||||
}
|
||||
gamepad->abs_info.emplace(code, std::move(info));
|
||||
return true;
|
||||
}
|
||||
|
||||
class LinuxGamepadService {
|
||||
public:
|
||||
|
|
@ -63,10 +92,10 @@ class LinuxGamepadService {
|
|||
void ScanForDevices();
|
||||
void AddMonitor();
|
||||
void RemoveMonitor();
|
||||
bool is_gamepad(struct udev_device* dev);
|
||||
bool IsDeviceGamepad(struct udev_device* dev);
|
||||
void ReadUdevChange();
|
||||
|
||||
// handler for data from /dev/input/jsN
|
||||
// handler for data from /dev/input/eventN
|
||||
static gboolean OnGamepadData(GIOChannel* source, GIOCondition condition,
|
||||
gpointer data);
|
||||
|
||||
|
|
@ -114,37 +143,143 @@ void LinuxGamepadService::AddDevice(struct udev_device* dev) {
|
|||
g_io_channel_set_encoding(channel, nullptr, nullptr);
|
||||
g_io_channel_set_buffered(channel, FALSE);
|
||||
int fd = g_io_channel_unix_get_fd(channel);
|
||||
char name[128];
|
||||
if (ioctl(fd, JSIOCGNAME(sizeof(name)), &name) == -1) {
|
||||
strcpy(name, "unknown");
|
||||
|
||||
input_id id{0};
|
||||
if (ioctl(fd, EVIOCGID, &id) < 0) {
|
||||
return;
|
||||
}
|
||||
const char* vendor_id =
|
||||
mUdev.udev_device_get_property_value(dev, "ID_VENDOR_ID");
|
||||
const char* model_id =
|
||||
mUdev.udev_device_get_property_value(dev, "ID_MODEL_ID");
|
||||
if (!vendor_id || !model_id) {
|
||||
struct udev_device* parent =
|
||||
mUdev.udev_device_get_parent_with_subsystem_devtype(dev, "input",
|
||||
nullptr);
|
||||
if (parent) {
|
||||
vendor_id = mUdev.udev_device_get_sysattr_value(parent, "id/vendor");
|
||||
model_id = mUdev.udev_device_get_sysattr_value(parent, "id/product");
|
||||
|
||||
char name[128]{0};
|
||||
if (ioctl(fd, EVIOCGNAME(sizeof(name)), &name) < 0) {
|
||||
strcpy(name, "Unknown Device");
|
||||
}
|
||||
|
||||
SprintfLiteral(gamepad->idstring, "%04" PRIx16 "-%04" PRIx16 "-%s", id.vendor,
|
||||
id.product, name);
|
||||
|
||||
unsigned long keyBits[BITS_TO_LONGS(KEY_CNT)] = {0};
|
||||
unsigned long absBits[BITS_TO_LONGS(ABS_CNT)] = {0};
|
||||
if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 ||
|
||||
ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Here, we try to support even strange cases where proper semantic
|
||||
* BTN_GAMEPAD button are combined with arbitrary extra buttons. */
|
||||
|
||||
/* These are mappings where the index is a CanonicalButtonIndex and the value
|
||||
* is an evdev code */
|
||||
const std::array<uint16_t, BUTTON_INDEX_COUNT> kStandardButtons = {
|
||||
/* BUTTON_INDEX_PRIMARY = */ BTN_SOUTH,
|
||||
/* BUTTON_INDEX_SECONDARY = */ BTN_EAST,
|
||||
/* BUTTON_INDEX_TERTIARY = */ BTN_WEST,
|
||||
/* BUTTON_INDEX_QUATERNARY = */ BTN_NORTH,
|
||||
/* BUTTON_INDEX_LEFT_SHOULDER = */ BTN_TL,
|
||||
/* BUTTON_INDEX_RIGHT_SHOULDER = */ BTN_TR,
|
||||
/* BUTTON_INDEX_LEFT_TRIGGER = */ BTN_TL2,
|
||||
/* BUTTON_INDEX_RIGHT_TRIGGER = */ BTN_TR2,
|
||||
/* BUTTON_INDEX_BACK_SELECT = */ BTN_SELECT,
|
||||
/* BUTTON_INDEX_START = */ BTN_START,
|
||||
/* BUTTON_INDEX_LEFT_THUMBSTICK = */ BTN_THUMBL,
|
||||
/* BUTTON_INDEX_RIGHT_THUMBSTICK = */ BTN_THUMBR,
|
||||
/* BUTTON_INDEX_DPAD_UP = */ BTN_DPAD_UP,
|
||||
/* BUTTON_INDEX_DPAD_DOWN = */ BTN_DPAD_DOWN,
|
||||
/* BUTTON_INDEX_DPAD_LEFT = */ BTN_DPAD_LEFT,
|
||||
/* BUTTON_INDEX_DPAD_RIGHT = */ BTN_DPAD_RIGHT,
|
||||
/* BUTTON_INDEX_META = */ BTN_MODE,
|
||||
};
|
||||
const std::array<uint16_t, AXIS_INDEX_COUNT> kStandardAxes = {
|
||||
/* AXIS_INDEX_LEFT_STICK_X = */ ABS_X,
|
||||
/* AXIS_INDEX_LEFT_STICK_Y = */ ABS_Y,
|
||||
/* AXIS_INDEX_RIGHT_STICK_X = */ ABS_RX,
|
||||
/* AXIS_INDEX_RIGHT_STICK_Y = */ ABS_RY,
|
||||
};
|
||||
|
||||
/*
|
||||
* According to https://www.kernel.org/doc/html/latest/input/gamepad.html,
|
||||
* "All gamepads that follow the protocol described here map BTN_GAMEPAD",
|
||||
* so we can use it as a proxy for semantic buttons in general. If it's
|
||||
* enabled, we're probably going to be acting like a standard gamepad
|
||||
*/
|
||||
uint32_t numButtons = 0;
|
||||
if (TestBit(keyBits, BTN_GAMEPAD)) {
|
||||
gamepad->isStandardGamepad = true;
|
||||
for (uint8_t button = 0; button < BUTTON_INDEX_COUNT; button++) {
|
||||
gamepad->key_map[kStandardButtons[button]] = button;
|
||||
}
|
||||
numButtons = BUTTON_INDEX_COUNT;
|
||||
}
|
||||
|
||||
// Now, go through the non-semantic buttons and handle them as extras
|
||||
for (uint16_t key = 0; key < KEY_MAX; key++) {
|
||||
// Skip standard buttons
|
||||
if (gamepad->isStandardGamepad &&
|
||||
std::find(kStandardButtons.begin(), kStandardButtons.end(), key) !=
|
||||
kStandardButtons.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TestBit(keyBits, key)) {
|
||||
gamepad->key_map[key] = numButtons++;
|
||||
}
|
||||
}
|
||||
snprintf(gamepad->idstring, sizeof(gamepad->idstring), "%s-%s-%s",
|
||||
vendor_id ? vendor_id : "unknown", model_id ? model_id : "unknown",
|
||||
name);
|
||||
|
||||
char numAxes = 0, numButtons = 0;
|
||||
ioctl(fd, JSIOCGAXES, &numAxes);
|
||||
gamepad->numAxes = numAxes;
|
||||
ioctl(fd, JSIOCGBUTTONS, &numButtons);
|
||||
gamepad->numButtons = numButtons;
|
||||
uint32_t numAxes = 0;
|
||||
if (gamepad->isStandardGamepad) {
|
||||
for (uint8_t i = 0; i < AXIS_INDEX_COUNT; i++) {
|
||||
gamepad->abs_map[kStandardAxes[i]] = i;
|
||||
LoadAbsInfo(fd, gamepad.get(), kStandardAxes[i]);
|
||||
}
|
||||
numAxes = AXIS_INDEX_COUNT;
|
||||
|
||||
gamepad->handle = service->AddGamepad(
|
||||
gamepad->idstring, mozilla::dom::GamepadMappingType::_empty,
|
||||
mozilla::dom::GamepadHand::_empty, gamepad->numButtons, gamepad->numAxes,
|
||||
0, 0, 0); // TODO: Bug 680289, implement gamepad haptics for Linux.
|
||||
// These are not real axis and get remapped to buttons.
|
||||
LoadAbsInfo(fd, gamepad.get(), ABS_HAT0X);
|
||||
LoadAbsInfo(fd, gamepad.get(), ABS_HAT0Y);
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < ABS_MAX; ++i) {
|
||||
if (gamepad->isStandardGamepad &&
|
||||
(std::find(kStandardAxes.begin(), kStandardAxes.end(), i) !=
|
||||
kStandardAxes.end() ||
|
||||
i == ABS_HAT0X || i == ABS_HAT0Y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (TestBit(absBits, i)) {
|
||||
if (LoadAbsInfo(fd, gamepad.get(), i)) {
|
||||
gamepad->abs_map[i] = numAxes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numAxes == 0) {
|
||||
NS_WARNING("Gamepad with zero axes detected?");
|
||||
}
|
||||
if (numButtons == 0) {
|
||||
NS_WARNING("Gamepad with zero buttons detected?");
|
||||
}
|
||||
|
||||
// NOTE: This almost always true, so we basically never use the remapping
|
||||
// code.
|
||||
if (gamepad->isStandardGamepad) {
|
||||
gamepad->handle =
|
||||
service->AddGamepad(gamepad->idstring, GamepadMappingType::Standard,
|
||||
GamepadHand::_empty, numButtons, numAxes, 0, 0, 0);
|
||||
} else {
|
||||
bool defaultRemapper = false;
|
||||
RefPtr<GamepadRemapper> remapper =
|
||||
GetGamepadRemapper(id.vendor, id.product, defaultRemapper);
|
||||
MOZ_ASSERT(remapper);
|
||||
remapper->SetAxisCount(numAxes);
|
||||
remapper->SetButtonCount(numButtons);
|
||||
|
||||
gamepad->handle = service->AddGamepad(
|
||||
gamepad->idstring, remapper->GetMappingType(), GamepadHand::_empty,
|
||||
remapper->GetButtonCount(), remapper->GetAxisCount(), 0,
|
||||
remapper->GetLightIndicatorCount(), remapper->GetTouchEventCount());
|
||||
gamepad->remapper = remapper.forget();
|
||||
}
|
||||
// TODO: Bug 680289, implement gamepad haptics for Linux.
|
||||
// TODO: Bug 1523355, implement gamepad lighindicator and touch for Linux.
|
||||
|
||||
gamepad->source_id =
|
||||
|
|
@ -192,7 +327,7 @@ void LinuxGamepadService::ScanForDevices() {
|
|||
const char* path = mUdev.udev_list_entry_get_name(dev_list_entry);
|
||||
struct udev_device* dev =
|
||||
mUdev.udev_device_new_from_syspath(mUdev.udev, path);
|
||||
if (is_gamepad(dev)) {
|
||||
if (IsDeviceGamepad(dev)) {
|
||||
AddDevice(dev);
|
||||
}
|
||||
|
||||
|
|
@ -235,7 +370,9 @@ void LinuxGamepadService::RemoveMonitor() {
|
|||
|
||||
void LinuxGamepadService::Startup() {
|
||||
// Don't bother starting up if libudev couldn't be loaded or initialized.
|
||||
if (!mUdev) return;
|
||||
if (!mUdev) {
|
||||
return;
|
||||
}
|
||||
|
||||
AddMonitor();
|
||||
ScanForDevices();
|
||||
|
|
@ -249,25 +386,23 @@ void LinuxGamepadService::Shutdown() {
|
|||
RemoveMonitor();
|
||||
}
|
||||
|
||||
bool LinuxGamepadService::is_gamepad(struct udev_device* dev) {
|
||||
if (!mUdev.udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK"))
|
||||
bool LinuxGamepadService::IsDeviceGamepad(struct udev_device* aDev) {
|
||||
if (!mUdev.udev_device_get_property_value(aDev, "ID_INPUT_JOYSTICK")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* devpath = mUdev.udev_device_get_devnode(dev);
|
||||
const char* devpath = mUdev.udev_device_get_devnode(aDev);
|
||||
if (!devpath) {
|
||||
return false;
|
||||
}
|
||||
if (strncmp(kJoystickPath, devpath, sizeof(kJoystickPath) - 1) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return strncmp(devpath, kEvdevPath, strlen(kEvdevPath)) == 0;
|
||||
}
|
||||
|
||||
void LinuxGamepadService::ReadUdevChange() {
|
||||
struct udev_device* dev = mUdev.udev_monitor_receive_device(mMonitor);
|
||||
const char* action = mUdev.udev_device_get_action(dev);
|
||||
if (is_gamepad(dev)) {
|
||||
if (IsDeviceGamepad(dev)) {
|
||||
const char* action = mUdev.udev_device_get_action(dev);
|
||||
if (strcmp(action, "add") == 0) {
|
||||
AddDevice(dev);
|
||||
} else if (strcmp(action, "remove") == 0) {
|
||||
|
|
@ -289,10 +424,12 @@ gboolean LinuxGamepadService::OnGamepadData(GIOChannel* source,
|
|||
auto* gamepad = static_cast<Gamepad*>(data);
|
||||
|
||||
// TODO: remove gamepad?
|
||||
if (condition & G_IO_ERR || condition & G_IO_HUP) return FALSE;
|
||||
if (condition & (G_IO_ERR | G_IO_HUP)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
struct js_event event;
|
||||
struct input_event event {};
|
||||
gsize count;
|
||||
GError* err = nullptr;
|
||||
if (g_io_channel_read_chars(source, (gchar*)&event, sizeof(event), &count,
|
||||
|
|
@ -301,19 +438,47 @@ gboolean LinuxGamepadService::OnGamepadData(GIOChannel* source,
|
|||
break;
|
||||
}
|
||||
|
||||
// TODO: store device state?
|
||||
if (event.type & JS_EVENT_INIT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case JS_EVENT_BUTTON:
|
||||
service->NewButtonEvent(gamepad->handle, event.number, !!event.value);
|
||||
break;
|
||||
case JS_EVENT_AXIS:
|
||||
service->NewAxisMoveEvent(gamepad->handle, event.number,
|
||||
((float)event.value) / kMaxAxisValue);
|
||||
case EV_KEY:
|
||||
if (gamepad->isStandardGamepad) {
|
||||
service->NewButtonEvent(gamepad->handle, gamepad->key_map[event.code],
|
||||
!!event.value);
|
||||
} else {
|
||||
gamepad->remapper->RemapButtonEvent(
|
||||
gamepad->handle, gamepad->key_map[event.code], !!event.value);
|
||||
}
|
||||
break;
|
||||
case EV_ABS: {
|
||||
if (!gamepad->abs_info.count(event.code)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double scaledValue =
|
||||
ScaleAxis(gamepad->abs_info[event.code], event.value);
|
||||
if (gamepad->isStandardGamepad) {
|
||||
switch (event.code) {
|
||||
case ABS_HAT0X:
|
||||
service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_DPAD_LEFT,
|
||||
AxisNegativeAsButton(scaledValue));
|
||||
service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_DPAD_RIGHT,
|
||||
AxisPositiveAsButton(scaledValue));
|
||||
break;
|
||||
case ABS_HAT0Y:
|
||||
service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_DPAD_UP,
|
||||
AxisNegativeAsButton(scaledValue));
|
||||
service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_DPAD_DOWN,
|
||||
AxisPositiveAsButton(scaledValue));
|
||||
break;
|
||||
default:
|
||||
service->NewAxisMoveEvent(
|
||||
gamepad->handle, gamepad->abs_map[event.code], scaledValue);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
gamepad->remapper->RemapAxisMoveEvent(
|
||||
gamepad->handle, gamepad->abs_map[event.code], scaledValue);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -324,7 +489,9 @@ gboolean LinuxGamepadService::OnGamepadData(GIOChannel* source,
|
|||
gboolean LinuxGamepadService::OnUdevMonitor(GIOChannel* source,
|
||||
GIOCondition condition,
|
||||
gpointer data) {
|
||||
if (condition & G_IO_ERR || condition & G_IO_HUP) return FALSE;
|
||||
if (condition & (G_IO_ERR | G_IO_HUP)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gService->ReadUdevChange();
|
||||
return TRUE;
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
|
|||
UNIFIED_SOURCES += ["windows/WindowsGamepad.cpp"]
|
||||
elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
|
||||
UNIFIED_SOURCES += ["android/AndroidGamepad.cpp"]
|
||||
elif CONFIG["OS_ARCH"] == "Linux":
|
||||
elif CONFIG["OS_ARCH"] in ("Linux", "FreeBSD", "DragonFly"):
|
||||
UNIFIED_SOURCES += ["linux/LinuxGamepad.cpp"]
|
||||
else:
|
||||
UNIFIED_SOURCES += ["fallback/FallbackGamepad.cpp"]
|
||||
|
|
|
|||
Loading…
Reference in a new issue