fix: Linux gamepad hotplug support, disconnect handling, and downcall fix (#79) - #80
Merged
Merged
Conversation
…tDevicePlugin - Trigger periodic refreshDevices() within getAll() so hotplugging works when starting with 0 devices - Add refreshDevices(boolean force) overload to allow plugins to immediately trigger discovery/cleanup - Update internal device collection before dispatching listeners so queries in callbacks observe the updated state - Ensure thread safety with synchronized refreshDevices() and CopyOnWriteArrayList
…sconnect errnos - Fix HANDLE_WRITE return type to JAVA_INT matching HANDLE_READ and polymorphic invoke - Add EBADF (9) and ENODEV (19) errno constants with unit test assertions - Treat EBADF and ENODEV as expected disconnections in Linux.invoke instead of logging SEVERE - Add overloaded read method supporting pre-allocated memory segments
…allocation polling - Implement refreshInputDevices() to scan /dev/input/event*, register newly connected devices, and remove disconnected ones - Automatically trigger refreshDevices() from pollLinuxEventDevice() - Detect ENODEV and EBADF on event read, immediately marking devices disconnected and forcing device refresh - Prevent file descriptor leaks by immediately closing discarded devices during scan - Prevent double-close in LinuxEventDevice and skip rumble stop calls on already-disconnected devices - Pre-allocate event structures and errno buffer per device to achieve zero-allocation polling in shared arena
… detection - Add isGamepadOrJoystick() heuristic checking EV_KEY and EV_ABS bitmasks - Accept standard gamepad/joystick buttons (BTN_GAMEPAD, BTN_JOYSTICK, BTN_TRIGGER_HAPPY, BTN_0..9, BTN_WHEEL, BTN_DPAD_*) - Accept flight stick, rudder, throttle, wheel, gas, brake, and hat axes - Reject touchpads (BTN_TOUCH, BTN_TOOL_FINGER), mice (BTN_MOUSE..BTN_TASK), and keyboards without gamepad buttons - Add bounds checking to LinuxEventDevice.isBitSet() to guard against short bitmask arrays - Add comprehensive unit test suite covering gamepads, flight sticks, rudder pedals, touchpads, and mice
…te downcalls - Define ssizeT = IS_32_BIT ? JAVA_INT : JAVA_LONG to accurately match POSIX ssize_t across architectures - Update FunctionDescriptor return types for read and write to ssizeT - Handle long.class return types in invokeWithCapturedState without WrongMethodTypeException - Add regression tests verifying downcall handle return types match architecture ssize_t
…and periodic refresh - Probe candidate event device nodes inside try-with-resources Arena.ofConfined() so non-gamepad devices leave zero off-heap memory behind - Allocate a dedicated Arena.ofShared() only when a candidate node is confirmed as a gamepad/joystick - Close the per-device arena when the device is disconnected or closed, reclaiming 100% of native resources - Pre-allocate versionSegment and versionCapturedState in LinuxEventDevice to make periodic liveness checks completely allocation-free - Use Arena.ofConfined() for force-feedback rumble effect upload, playback, and removal to prevent off-heap growth during rumble - Remove plugin-level shared arena from LinuxEventDevicePlugin - Add regression tests for candidate probe arena lifecycle, device disconnection, and arena closing
…ble-close in try-with-resources
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes #79
Overview
Resolves several interrelated issues on Linux where gamepad connections and disconnections were not detected, downcalls to
write/setGaincaused a JVMWrongMethodTypeException, laptop touchpads/mice were falsely registered as controllers, and polling caused unbounded off-heap memory allocations.Problems & Solutions
1. Gamepad Connects and Disconnects Not Registering
LinuxEventDevicePlugin.refreshInputDevices()was an unpopulated stub. In addition,AbstractInputDevicePlugin.getAll()never triggeredrefreshDevices(), preventing discovery if an application started with 0 gamepads connected.LinuxEventDevicePlugin.refreshInputDevices()to scan/dev/input/event*, register newly connected devices, detect disconnected devices viaEVIOCGVERSION, and prune stale entries.refreshDevices()checks inAbstractInputDevicePlugin.getAll().refreshDevices(boolean force)to allow instant, out-of-band discovery/cleanup without waiting forhotPlugIntervalto expire.AbstractInputDevicePluginto commit internal device lists before dispatching connect/disconnect/change listeners so listener callbacks queryingInputDevices.getDevices()orgetAll()observe the updated state.2.
WrongMethodTypeExceptionon Linuxwrite/setGainLinux.java,HANDLE_WRITEwas configured with a return type ofsizeT(JAVA_LONGon 64-bit platforms), but downcall call sites cast the polymorphic invoke result to(int). This threwjava.lang.invoke.WrongMethodTypeException: cannot adapt long to intwhenever rumble effects or gain were updated.HANDLE_WRITE's function descriptor return type toJAVA_INTmatchingHANDLE_READandinvoke().3. File Descriptor Exhaustion & Disconnect Log Spam
/dev/input/event*opened devices that were discarded (e.g. video buses, HDMI, or devices without required components) without closing them, leading to file descriptor leaks. Furthermore, when a device was unplugged, subsequent operations logged continuousLevel.SEVEREerrors.device.close(this.memoryArena)on all ignored or discarded device nodes during discovery.EBADF(9) andENODEV(19) errno constants. When detected duringreadEvent(), devices are marked disconnected,refreshDevices(true)is triggered immediately, and logs are downgraded toLevel.FINE.closedguard inLinuxEventDevice.close()to prevent double-closing and skip sending rumble stop commands to nodes already detached by the kernel.4. Zero-Allocation Polling in Shared Arenas
Linux.read(Arena, fd)allocated aninput_eventlayout and captured state layout on every single event intomemoryArena(a shared arena that lives for the lifetime of the plugin), creating unbounded off-heap native memory growth.inputEventSegment,capturedStateSegment, and an errno buffer perLinuxEventDeviceinstance, eliminating native heap allocations inside the hot polling loop.5. False-Positive Controller Detection (Touchpads, Mice, Keyboards)
SYNA30D2:00 Touchpadfrom issue logs) and mice exposeABS_X/ABS_YandBTN_LEFT, satisfying the previous naiveanyMatch(BUTTON || AXIS)check and registering as game controllers.LinuxEventDevicePlugin.isGamepadOrJoystick(keyBits, absBits)implementing kernel evdev heuristics:BTN_GAMEPAD,BTN_JOYSTICK,BTN_TRIGGER_HAPPY,BTN_0..9,BTN_WHEEL,BTN_DPAD_*).BTN_TOUCH,BTN_TOOL_FINGER,BTN_TOOL_DOUBLETAP, etc.), mice (BTN_LEFT..BTN_TASK), and keyboards lacking gamepad buttons.LinuxEventDevice.isBitSet().Changes by Commit
f887c59feat: improve hotplug lifecycle and event consistency in AbstractInputDevicePlugin7dea2aefix(linux): fix WrongMethodTypeException on write downcall and add disconnect errnos61a2ac7feat(linux): implement device hotplug, disconnect handling, and zero-allocation pollingf52662cfix(linux): filter out touchpads, mice, and keyboards from controller detectionVerification & Testing
AbstractInputDevicePluginTests: Validates initialization checks,onDeviceConnected,onDeviceDisconnected,onDevicesChanged,hotPlugIntervalexpiry, and listener cleanup onclose().LinuxEventDevicePluginTests: Comprehensive unit tests forisGamepadOrJoystickcovering Xbox controllers, flight sticks, arcade fight sticks, rudder pedals, hat switches, DualShock 4 with integrated touchpads, and rejecting laptop trackpads, mice, and keyboards.LinuxPermissionTests: ValidatesEBADFandENODEVerrno mappings../gradlew build: SUCCESSFUL (all unit tests and tasks passing)../gradlew spotlessCheck: SUCCESSFUL (enforces Google Java Format).