diff --git a/www/firefox/Makefile b/www/firefox/Makefile index 0214820bf4a0..30556e7188b7 100644 --- a/www/firefox/Makefile +++ b/www/firefox/Makefile @@ -1,60 +1,61 @@ # Created by: Alan Eldridge PORTNAME= firefox DISTVERSION= 88.0 +PORTREVISION= 1 PORTEPOCH= 2 CATEGORIES= www MASTER_SITES= MOZILLA/${PORTNAME}/releases/${DISTVERSION}/source \ MOZILLA/${PORTNAME}/candidates/${DISTVERSION}-candidates/build2/source DISTFILES= ${DISTNAME}.source${EXTRACT_SUFX} MAINTAINER= gecko@FreeBSD.org COMMENT= Web browser based on the browser portion of Mozilla BUILD_DEPENDS= nspr>=4.26:devel/nspr \ nss>=3.62:security/nss \ icu>=67.1,1:devel/icu \ libevent>=2.1.8:devel/libevent \ harfbuzz>=2.7.4:print/harfbuzz \ graphite2>=1.3.14:graphics/graphite2 \ png>=1.6.35:graphics/png \ libvpx>=1.8.2:multimedia/libvpx \ ${PYTHON_PKGNAMEPREFIX}sqlite3>0:databases/py-sqlite3@${PY_FLAVOR} \ v4l_compat>0:multimedia/v4l_compat \ autoconf-2.13:devel/autoconf213 \ nasm:devel/nasm \ yasm:devel/yasm \ zip:archivers/zip USE_GECKO= gecko CONFLICTS_INSTALL= firefox-esr USE_MOZILLA= -sqlite CFLAGS_powerpc64le= -DSQLITE_BYTEORDER=1234 USES= tar:xz FIREFOX_ICON= ${MOZILLA}.png FIREFOX_ICON_SRC= ${PREFIX}/lib/${MOZILLA}/browser/chrome/icons/default/default48.png FIREFOX_DESKTOP= ${MOZSRC}/taskcluster/docker/${MOZILLA}-snap/${MOZILLA}.desktop MOZ_OPTIONS= --enable-application=browser \ --enable-official-branding .include "${.CURDIR}/../../www/firefox/Makefile.options" post-patch: @${REINPLACE_CMD} -e 's/%u/%U/' -e '/X-MultipleArgs/d' \ -e '/^Icon/s/=.*/=${FIREFOX_ICON:R}/' \ ${FIREFOX_DESKTOP} @${REINPLACE_CMD} -e 's|%%LOCALBASE%%|${LOCALBASE}|g' \ ${WRKSRC}/browser/app/nsBrowserApp.cpp pre-configure: (cd ${WRKSRC} && ${LOCALBASE}/bin/autoconf-2.13) (cd ${WRKSRC}/js/src/ && ${LOCALBASE}/bin/autoconf-2.13) post-install: ${INSTALL_DATA} ${FIREFOX_DESKTOP} ${STAGEDIR}${PREFIX}/share/applications/ ${MKDIR} ${STAGEDIR}${PREFIX}/share/pixmaps ${LN} -sf ${FIREFOX_ICON_SRC} ${STAGEDIR}${PREFIX}/share/pixmaps/${FIREFOX_ICON} .include diff --git a/www/firefox/files/patch-bug1680982 b/www/firefox/files/patch-bug1680982 index 17604ac3b6a2..c9fc2344bb44 100644 --- a/www/firefox/files/patch-bug1680982 +++ b/www/firefox/files/patch-bug1680982 @@ -1,369 +1,388 @@ -commit d947b92c7503 +commit 3204512f58a1 Author: Greg V Date: Sun Dec 6 22:07:00 2020 +0000 - Bug 1680982 - Use evdev for gamepads on Linux/FreeBSD + Bug 1680982 - Use evdev instead of the Linux legacy joystick API for gamepads - Switch from the legacy Linux joystick API to the generic evdev API. + Using evdev is a prerequisite for adding rumble (haptic feedback) and LED support. - BTN_GAMEPAD semantic buttons are interpreted directly, since all kernel drivers are supposed to use them correctly: https://www.kernel.org/doc/html/latest/input/gamepad.html - BTN_JOYSTICK legacy style numbered buttons use the model specific remappers - - using evdev is a prerequisite for adding rumble (haptic feedback) and other extras - - the Linux gamepad module is enabled on FreeBSD, because - FreeBSD provides evdev, and libudev-devd provides enough of libudev + - we support even strange devices that combine both styles in one device + - the Linux gamepad module is enabled on FreeBSD and DragonFly, because + these kernels provide evdev, and libudev-devd provides enough of libudev (evdev headers are provided by the devel/evdev-proto package) Differential Revision: https://phabricator.services.mozilla.com/D98868 --- - dom/gamepad/linux/LinuxGamepad.cpp | 243 +++++++++++++++++++++++++++++++------ + dom/gamepad/linux/LinuxGamepad.cpp | 262 ++++++++++++++++++++++++++++++++----- dom/gamepad/moz.build | 2 +- - 2 files changed, 210 insertions(+), 35 deletions(-) + 2 files changed, 229 insertions(+), 35 deletions(-) diff --git dom/gamepad/linux/LinuxGamepad.cpp dom/gamepad/linux/LinuxGamepad.cpp -index 512ac765020d..0e51183c2a2d 100644 +index deee47b9d267..31f0aad7ae4a 100644 --- dom/gamepad/linux/LinuxGamepad.cpp +++ dom/gamepad/linux/LinuxGamepad.cpp @@ -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 +#include #include #include -#include +#include #include #include #include @@ -21,10 +22,14 @@ #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 "udev.h" +#define LONG_BITS (sizeof(long) * 8) +#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS) + namespace { using namespace mozilla::dom; @@ -36,19 +41,29 @@ 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, int bit) { + return !!(arr[bit / LONG_BITS] & (1LL << (bit % LONG_BITS))); +} + +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; + RefPtr 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 abs_info; +}; class LinuxGamepadService { public: @@ -66,7 +81,7 @@ class LinuxGamepadService { bool is_gamepad(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,8 +129,12 @@ void LinuxGamepadService::AddDevice(struct udev_device* dev) { +@@ -114,8 +129,14 @@ 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); + -+ struct input_id id = {0}; -+ ioctl(fd, EVIOCGID, &id); ++ struct input_id id {}; ++ if (ioctl(fd, EVIOCGID, &id) == -1) { ++ return; ++ } + char name[128]; - if (ioctl(fd, JSIOCGNAME(sizeof(name)), &name) == -1) { + if (ioctl(fd, EVIOCGNAME(sizeof(name)), &name) == -1) { strcpy(name, "unknown"); } const char* vendor_id = -@@ -131,20 +150,69 @@ void LinuxGamepadService::AddDevice(struct udev_device* dev) { +@@ -131,20 +152,86 @@ void LinuxGamepadService::AddDevice(struct udev_device* dev) { model_id = mUdev.udev_device_get_sysattr_value(parent, "id/product"); } } + if (!vendor_id && id.vendor != 0) { + vendor_id = (const char*)alloca(5); + snprintf((char*)vendor_id, 5, "%04x", id.vendor); + } + if (!model_id && id.product != 0) { + model_id = (const char*)alloca(5); + snprintf((char*)model_id, 5, "%04x", id.product); + } 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; + unsigned long key_bits[NLONGS(KEY_CNT)] = {0}; + unsigned long abs_bits[NLONGS(ABS_CNT)] = {0}; + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), key_bits); + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits); ++ ++ /* Here, we try to support even strange cases where proper semantic ++ * BTN_GAMEPAD button are combined with arbitrary extra buttons. */ + for (uint16_t i = BTN_JOYSTICK; i < KEY_MAX; i++) { -+ /* Skip proper gamepad events, they are handled directly */ ++ /* Do not map semantic buttons, they are handled directly */ + if (i == BTN_GAMEPAD) { + i = BTN_THUMBR + 1; + continue; + } + if (i == BTN_DPAD_UP) { + i = BTN_DPAD_RIGHT + 1; + continue; + } + if (TestBit(key_bits, i)) { + gamepad->key_map[i] = numButtons++; + } + } + for (uint16_t i = 0; i < BTN_JOYSTICK; i++) { + if (TestBit(key_bits, i)) { + gamepad->key_map[i] = numButtons++; + } + } ++ for (uint16_t i = BTN_GAMEPAD; i <= BTN_THUMBR; i++) { ++ /* But if any semantic event exists, count them all */ ++ if (TestBit(key_bits, i)) { ++ numButtons += BUTTON_INDEX_COUNT; ++ break; ++ } ++ } + for (uint16_t i = 0; i < ABS_MAX; ++i) { + if (TestBit(abs_bits, i)) { + gamepad->abs_info.emplace(i, input_absinfo{}); + if (ioctl(fd, EVIOCGABS(i), &gamepad->abs_info[i]) < 0) { + continue; + } + if (gamepad->abs_info[i].minimum == gamepad->abs_info[i].maximum) { + gamepad->abs_info.erase(i); + continue; + } + 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?"); ++ } ++ + bool defaultRemapper = false; + RefPtr remapper = + GetGamepadRemapper(id.vendor, id.product, defaultRemapper); + MOZ_ASSERT(remapper); + remapper->SetAxisCount(numAxes); + remapper->SetButtonCount(numButtons); 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. + 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 = -@@ -257,7 +325,7 @@ bool LinuxGamepadService::is_gamepad(struct udev_device* dev) { +@@ -257,7 +344,7 @@ bool LinuxGamepadService::is_gamepad(struct udev_device* dev) { if (!devpath) { return false; } - if (strncmp(kJoystickPath, devpath, sizeof(kJoystickPath) - 1) != 0) { + if (strncmp(kEvdevPath, devpath, sizeof(kEvdevPath) - 1) != 0) { return false; } -@@ -292,7 +360,7 @@ gboolean LinuxGamepadService::OnGamepadData(GIOChannel* source, +@@ -292,7 +379,7 @@ gboolean LinuxGamepadService::OnGamepadData(GIOChannel* source, if (condition & G_IO_ERR || condition & G_IO_HUP) return FALSE; while (true) { - struct js_event event; -+ struct input_event event = {0}; ++ struct input_event event {}; gsize count; GError* err = nullptr; if (g_io_channel_read_chars(source, (gchar*)&event, sizeof(event), &count, -@@ -301,18 +369,125 @@ gboolean LinuxGamepadService::OnGamepadData(GIOChannel* source, +@@ -301,18 +388,125 @@ 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); + case EV_KEY: + switch (event.code) { + /* The gamepad events are meaningful, and according to + * https://www.kernel.org/doc/html/latest/input/gamepad.html + * "No other devices, that do not look/feel like a gamepad, shall + * report these events" */ + case BTN_SOUTH: + service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_PRIMARY, + !!event.value); + break; + case BTN_EAST: + service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_SECONDARY, + !!event.value); + break; + case BTN_NORTH: + service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_QUATERNARY, + !!event.value); + break; + case BTN_WEST: + service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_TERTIARY, + !!event.value); + break; + case BTN_TL: + service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_LEFT_SHOULDER, + !!event.value); + break; + case BTN_TR: + service->NewButtonEvent(gamepad->handle, + BUTTON_INDEX_RIGHT_SHOULDER, !!event.value); + break; + case BTN_TL2: + service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_LEFT_TRIGGER, + !!event.value); + break; + case BTN_TR2: + service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_RIGHT_TRIGGER, + !!event.value); + break; + case BTN_SELECT: + service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_BACK_SELECT, + !!event.value); + break; + case BTN_START: + service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_START, + !!event.value); + break; + case BTN_MODE: + service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_META, + !!event.value); + break; + case BTN_THUMBL: + service->NewButtonEvent( + gamepad->handle, BUTTON_INDEX_LEFT_THUMBSTICK, !!event.value); + break; + case BTN_THUMBR: + service->NewButtonEvent( + gamepad->handle, BUTTON_INDEX_RIGHT_THUMBSTICK, !!event.value); + break; + case BTN_DPAD_UP: + service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_DPAD_UP, + !!event.value); + break; + case BTN_DPAD_DOWN: + service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_DPAD_DOWN, + !!event.value); + break; + case BTN_DPAD_LEFT: + service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_DPAD_LEFT, + !!event.value); + break; + case BTN_DPAD_RIGHT: + service->NewButtonEvent(gamepad->handle, BUTTON_INDEX_DPAD_RIGHT, + !!event.value); + break; + default: + /* For non-gamepad events, this is the "anything goes" numbered + * handling that should be handled with remappers. */ + gamepad->remapper->RemapButtonEvent( + gamepad->handle, gamepad->key_map[event.code], !!event.value); + break; + } break; - case JS_EVENT_AXIS: - service->NewAxisMoveEvent(gamepad->handle, event.number, - ((float)event.value) / kMaxAxisValue); + case EV_ABS: + if (!gamepad->abs_info.count(event.code)) continue; + switch (event.code) { + case ABS_HAT0X: + service->NewButtonEvent( + gamepad->handle, BUTTON_INDEX_DPAD_LEFT, + AxisNegativeAsButton( + ScaleAxis(gamepad->abs_info[event.code], event.value))); + service->NewButtonEvent( + gamepad->handle, BUTTON_INDEX_DPAD_RIGHT, + AxisPositiveAsButton( + ScaleAxis(gamepad->abs_info[event.code], event.value))); + break; + case ABS_HAT0Y: + service->NewButtonEvent( + gamepad->handle, BUTTON_INDEX_DPAD_UP, + AxisNegativeAsButton( + ScaleAxis(gamepad->abs_info[event.code], event.value))); + service->NewButtonEvent( + gamepad->handle, BUTTON_INDEX_DPAD_DOWN, + AxisPositiveAsButton( + ScaleAxis(gamepad->abs_info[event.code], event.value))); + break; + case ABS_HAT1X: + case ABS_HAT1Y: + case ABS_HAT2X: + case ABS_HAT2Y: + case ABS_HAT3X: + case ABS_HAT3Y: + break; + default: + gamepad->remapper->RemapAxisMoveEvent( + gamepad->handle, gamepad->abs_map[event.code], + ScaleAxis(gamepad->abs_info[event.code], event.value)); + break; + } break; } } diff --git dom/gamepad/moz.build dom/gamepad/moz.build -index 5f55d5a95e96..b5d10e9d095a 100644 +index 5f55d5a95e96..544b7f927736 100644 --- dom/gamepad/moz.build +++ dom/gamepad/moz.build @@ -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"] == "Linux" or CONFIG["OS_ARCH"] == "FreeBSD": ++elif CONFIG["OS_ARCH"] in ("Linux", "FreeBSD", "DragonFly"): UNIFIED_SOURCES += ["linux/LinuxGamepad.cpp"] else: UNIFIED_SOURCES += ["fallback/FallbackGamepad.cpp"]