25 Commits

Author SHA1 Message Date
Adam Shiervani df5dbea431 fix(keyboard): keep modifiers out of auto-release (#1438)
* fix(keyboard): keep modifiers out of auto-release
Prevent per-key auto-release from dropping held modifiers during jitter while keeping explicit cleanup paths covered by E2E tests.

* fix(keyboard): keep modifiers out of auto-release
Prevent per-key auto-release from dropping held modifiers during jitter while keeping explicit cleanup paths covered by E2E tests.

* chore(keyboard): trim autorelease comments

Keep comments focused on keyboard behavior and remove branch-specific narrative from the tests.

* fix(keyboard): reset keepalive timing on key state changes

Reset session keepalive timing on every keyboard state change so stale gaps do not poison later holds under modifiers.
2026-05-01 14:48:25 +02:00
Adam Shiervani 73a67e88ab fix(mouse): prevent double wheel scroll by sending to only one HID device (#1390)
rpcWheelReport was unconditionally sending wheel events to both the
absolute and relative mouse HID devices. The host OS sees two separate
USB HID devices each producing a scroll event, so it processes both,
doubling the effective scroll distance (e.g. 6 lines instead of 3 on
Windows).

Send wheel events to the absolute mouse device when enabled, falling
back to the relative mouse device otherwise. This mirrors how pointer
reports already work (absMouseReport vs relMouseReport are separate).

Add an E2E test that verifies exactly one wheel event is received per
wheelReport RPC call when both mouse devices are enabled.
2026-04-01 10:58:58 +02:00
Adam Shiervani 203c6ae6fd fix: recover HID chardev after DWC3 rebind race on RV1106 (#1366)
* fix: reset USB gadget when virtual media unmount fails with EBUSY (#834)

When unmountImageLocked() gets EBUSY from the kernel (host OS still
accessing the virtual disk via PREVENT MEDIUM REMOVAL), fall back to
gadget.RebindUsb(true) to force-disconnect the host, then retry the
unmount. Uses RebindUsb directly instead of UpdateGadgetConfig to avoid
hitting the same EBUSY when writing configfs attributes before rebind.

After rebind, properly reopen keyboard HID file (ResetHIDFiles + sleep +
OpenKeyboardHidFile) matching the pattern in setMassStorageMode().

Also propagate unmount errors to RPC callers and only clear
currentVirtualMediaState after the unmount actually succeeds.

Adds E2E test that mounts an ISO on the remote host to trigger PREVENT
MEDIUM REMOVAL, then verifies unmount succeeds and keyboard recovers.

* fix: recover HID chardev after DWC3 rebind race on RV1106

The DWC3 USB controller on the RV1106 has a race condition where rapid
unbind→bind of the UDC can permanently corrupt HID chardev state —
/dev/hidg0 returns ENXIO even though the device node exists and the UDC
shows "configured". This can be triggered by UpdateGadgetConfig's
transaction rebind and by host-initiated USB device resets during mass
storage media changes.

Three-part fix:

1. rebindUsb(): after binding, verify /dev/hidg0 is openable. If not,
   unbind again with a 100ms pause for kernel cleanup, then rebind.

2. setMassStorageMode(): pre-set recovery timer before UpdateGadgetConfig
   to prevent the poller from interfering. After the 1s sleep, if
   OpenKeyboardHidFile fails, do a corrective RebindUsb + retry.

3. checkUSBState() poller: when a state transition occurs and
   OpenKeyboardHidFile fails, trigger a corrective rebind to recover
   from host-initiated USB resets that corrupt the chardev.

* fix: suppress USB recovery poller before rebind in unmount and mode-change paths

The auto-recovery poller could see transient "not attached" UDC state
during RebindUsb and trigger a competing rebind, corrupting HID chardev
state. Add setUSBRecoveryTimer calls before the rebind in
unmountImageLocked and before the corrective rebind in setMassStorageMode.

* test: replace blind sleep with two-phase wait in factory-reset e2e test

Wait for device to become unreachable before polling for it to come back,
preventing false passes from stale pre-reset responses.

* refactor: simplify branch — extract helpers, remove duplication, fix flaky tests

- Extract rebindAndRecoverHID() in Go to deduplicate USB recovery sequences
- Remove redundant setUSBRecoveryTimer() call after UpdateGadgetConfig()
- Extract waitForKeyboardReady() helper replacing 5 duplicate retry loops
- Consolidate 3 duplicate remoteExec definitions into single remoteHostExec()
- Use shared SSH_OPTS from helpers.ts instead of hardcoded SSH options
- Fix remote agent omitempty on mouse X/Y causing undefined in TypeScript
- Poll keys-down state in disconnect test to avoid race condition

* fix: remove dead IsHidgChardevHealthy export, reset HID files before rebind

- Remove unused exported IsHidgChardevHealthy wrapper (only the unexported
  isHidgChardevHealthy is called, inside rebindUsb)
- Move ResetHIDFiles() before RebindUsb in checkUSBState so stale file
  handles are closed even if the rebind fails — prevents silent mouse
  write failures on dead inodes after a successful unbind + failed bind
2026-03-28 20:56:21 +01:00
Adam Shiervani cf7215411d feat: add horizontal mouse wheel scrolling support (#415) (#1358)
* fix: add horizontal mouse wheel (AC Pan) scroll support (#415)

- HID descriptors: add AC Pan (Usage 0x0238, Consumer Page) to both
  absolute and relative mouse descriptors for horizontal scroll
- Backend: extend AbsMouseWheelReport to accept wheelX, add
  RelMouseWheelReport with both axes, update report_length
- RPC: add wheelX parameter to wheelReport binding
- Frontend: read deltaX in wheel handler with same clamping/inversion
  and throttling as vertical scroll
- E2E: add wheel scroll test verifying both vertical (REL_WHEEL) and
  horizontal (REL_HWHEEL) events reach the remote host

* style: fix goimports alignment in RelMouseReport

* fix: don't negate horizontal scroll direction in wheelReport

The clampWheel helper was negating the result for both axes, but only
vertical scrolling needs inversion (browser deltaY and HID Wheel use
opposite sign conventions). Horizontal scrolling (deltaX / AC Pan)
shares the same convention (positive = right), so negation reversed the
direction on the target machine.

* fix: wire RelMouseWheelReport into RPC and add wheel scroll e2e tests

rpcWheelReport only called AbsMouseWheelReport, so wheel scrolling was
silently broken in relative-only mouse mode. Now calls both Abs and Rel
wheel report methods (each guards on its own enabledDevices flag).

Adds e2e tests for vertical/horizontal wheel scroll in default mode and
relative-only mode. Bumps beforeAll waitForInputDevices timeout to 30s
and keyboard LED test expectKeyPress timeouts to 5s to reduce flakiness.
2026-03-27 14:10:55 +01:00
Adam Shiervani 15dc380062 fix: auto-recover USB gadget when host power-cycles (#1297)
* fix: auto-recover USB gadget when host reconnects (#128)

When the USB host reboots or disconnects, the UDC state becomes
"not attached" and never recovers. Add automatic recovery that detects
this state and rebinds the USB gadget, with rate limiting to avoid
thrashing. Also refactor keyboard HID file handling to support
force-reopen after rebind.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: move USB recovery logic to internal/usbgadget package

Extract ShouldAttemptUSBRecovery and its retry interval constant into
the internal/usbgadget package so the logic is testable without
importing the top-level kvm package. Reset the recovery timer in
updateUsbRelatedConfig to prevent auto-recovery from interfering
during the host's USB re-enumeration window after a deliberate
config change.

Made-with: Cursor

* feat(e2e): add JSON-RPC data channel support to test hooks

Expose the WebRTC RPC data channel through test hooks and add a
sendJsonRpc helper that sends a JSON-RPC request over the channel
and resolves via callback with timeout handling. This enables e2e
tests to invoke backend RPC methods directly.

Made-with: Cursor

* refactor(e2e): restructure tests with remote-agent suite

Move device-level e2e tests (config-reset, EDID, HTTPS, HDMI sleep,
LED, mouse, USB attach timing, USB device) into a consolidated
remote-agent suite that runs through a Go-based agent binary. Add a
separate Playwright project for remote-agent tests.

- Remove standalone spec files now covered by ra-all.spec.ts
- Rename login-rate-limit to zz-login-rate-limit so it runs last
  (avoids needing a post-test reboot to clear rate-limit state)
- Reorder welcome-password tests so validation runs first, reusing
  the onboarding state and saving an SSH reset cycle

Made-with: Cursor

* refactor(e2e): clean up test helpers and reduce duplication

Consolidate all test helpers into a single helpers.ts file, removing
the separate ota-helpers.ts. This gives every test file a single import
source and eliminates duplicated code across the test suite.

Key changes:
- Merge ota-helpers.ts into helpers.ts (mock server, binary deployment,
  device config, env var validation, triggerUpdate, withTempSignature)
- Remove duplicated rpc/restartAppViaSSH/waitForDeviceReady functions
  from ra-all.spec.ts in favour of shared imports
- Extract loginAndOpenSettings helper in settings-local-auth tests
- Extract getOTAEnvVars, toPreReleaseVersion, triggerUpdate, and
  withTempSignature to reduce boilerplate across OTA tests
- Remove unused verifyMouseWorks function and dead variables
- Strip redundant JSDoc that just restated type signatures
- Remove duplicated per-project use config from playwright.config.ts
  (already inherited from top-level)
- Convert dynamic imports in sshExec to top-level imports

Made-with: Cursor

* refactor(e2e): move binary deployment into Playwright globalSetup

Replace the shell-script deployment logic with Playwright's
globalSetup/globalTeardown hooks. When BASELINE_BINARY_PATH is set,
globalSetup deploys the binary, resets device config, reboots, and
captures pre-test logs. globalTeardown captures post-test logs.

This keeps the deployment lifecycle inside Playwright where it belongs,
and reduces test_core_e2e.sh to a thin wrapper that sets env vars.

Made-with: Cursor

* fix: retry HID file reopen after USB gadget rebind

After rebinding the DWC3 USB controller, the kernel needs a moment to
create the /dev/hidg* device nodes. The previous code attempted to
reopen the keyboard HID file immediately after rebind, which raced
with the kernel and failed with "no such device or address".

Add a retry loop (up to 10 attempts, 200ms apart) to wait for the
device nodes to appear before reopening the keyboard HID file.

Made-with: Cursor

* fix: harden USB gadget recovery after UDC unbind

Reset stale HID gadget handles after rebind, suppress transient HID-open errors during detach windows, and fall back to full gadget reconfiguration when simple UDC rebind does not restore keyboard HID promptly. Strengthen the remote-agent USB recovery E2E to verify both keyboard and mouse input recover after unbind with retry tolerance for host-side input node churn.

Made-with: Cursor

* refactor: simplify USB HID error handling and reduce hot-path overhead

- Use errors.Is with syscall.Errno instead of string matching in
  IsHIDTemporarilyUnavailableError (robust, zero-alloc)
- Cache USB state in usbReadyForHidReports instead of reading sysfs
  on every HID report
- Extract rpcHidReport wrapper to deduplicate 5 rpc*Report functions
- Fix openWithTimeout goroutine/fd leak on timeout
- Add USBStateNotAttached/USBStateUnknown constants, replace literals
- Deduplicate discoverJetKVMDevices by delegating to listInputDevices

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: run remote-agent e2e tests when JETKVM_REMOTE_HOST is provided

Made-with: Cursor

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 12:54:20 +01:00
Alex Howells 1c30bac4f1 fix(display): prevent background events from resetting off timer (#1277)
The display-off timer never fires when a WebRTC session is active because
background system events (video state changes, session count updates, USB
state changes, network state changes) all call requestDisplayUpdate with
shouldWakeDisplay=true. Each wake resets both the dim and off tickers via
wakeDisplay, so the off timer restarts from zero on every event and never
reaches its threshold.

Only physical input device events (touchscreen taps, reported as
indev_event) should wake the display and reset the timers. Background
state updates still need to refresh the LCD content but should not
interfere with the backlight timeout cycle.

Change requestDisplayUpdate calls from background event handlers to pass
shouldWakeDisplay=false in native.go (video_state_changed), network.go
(network_state_changed), usb.go (usb_state_changed), and webrtc.go
(active_sessions_changed). Startup paths (init_display, native_restart)
retain shouldWakeDisplay=true.

Fixes #1133
2026-03-16 11:45:52 +01:00
Marc Brooks 1831f34ebf When adding/removing routes, honor scope and protocol (#984)
Added a lot more logging detail to find the route bugs which was that the gateways were being added to the map with their IP and netmask, then looked for by only the IP, which meant we never removed them.
Replace log context additions that did .Str("foo", foo.String()) with .Stringer("foo", foo) so the conversion to string is lazy.
2025-12-29 15:14:44 +01:00
Aveline 657a177462 feat: jetkvm native in cGo 2025-09-29 14:09:30 +02:00
Aveline afb146d78c feat: release keyPress automatically (#796)
* feat: release keyPress automatically

* send keepalive when pressing the key

* remove logging

* clean up logging

* chore: use unreliable channel to send keepalive events

* chore: use ordered unreliable channel for pointer events

* chore: adjust auto release key interval

* chore: update logging for kbdAutoReleaseLock

* chore: update comment for KEEPALIVE_INTERVAL

* fix: should cancelAutorelease when pressed is true

* fix: handshake won't happen if webrtc reconnects

* chore: add trace log for writeWithTimeout

* chore: add timeout for KeypressReport

* chore: use the proper key to send release command

* refactor: simplify HID RPC keyboard input handling and improve key state management

- Updated `handleHidRPCKeyboardInput` to return errors directly instead of keys down state.
- Refactored `rpcKeyboardReport` and `rpcKeypressReport` to return errors instead of states.
- Introduced a queue for managing key down state updates in the `Session` struct to prevent input handling stalls.
- Adjusted the `UpdateKeysDown` method to handle state changes more efficiently.
- Removed unnecessary logging and commented-out code for clarity.

* refactor: enhance keyboard auto-release functionality and key state management

* fix: correct Windows default auto-repeat delay comment from 1ms to 1s

* refactor: send keypress as early as possible

* refactor: replace console.warn with console.info for HID RPC channel events

* refactor: remove unused NewKeypressKeepAliveMessage function from HID RPC

* fix: handle error in key release process and log warnings

* fix: log warning on keypress report failure

* fix: update auto-release keyboard interval to 225

* refactor: enhance keep-alive handling and jitter compensation in HID RPC

- Implemented staleness guard to ignore outdated keep-alive packets.
- Added jitter compensation logic to adjust timer extensions based on packet arrival times.
- Introduced new methods for managing keep-alive state and reset functionality in the Session struct.
- Updated auto-release delay mechanism to use dynamic durations based on keep-alive timing.
- Adjusted keep-alive interval in the UI to improve responsiveness.

* gofmt

* clean up code

* chore: use dynamic duration for scheduleAutoRelease

* Use harcoded timer reset value for now

* fix: prevent nil pointer dereference when stopping timers in Close method

* refactor: remove nil check for kbdAutoReleaseTimers in DelayAutoReleaseWithDuration

* refactor: optimize dependencies in useHidRpc hooks

* refactor: streamline keep-alive timer management in useKeyboard hook

* refactor: clarify comments in useKeyboard hook for resetKeyboardState function

* refactor: reduce keysDownStateQueueSize

* refactor: close and reset keysDownStateQueue in newSession function

* chore: resolve conflicts

* resolve conflicts

---------

Co-authored-by: Adam Shiervani <adam.shiervani@gmail.com>
2025-09-18 13:35:47 +02:00
Aveline bcc307b147 feat: hid rpc channel (#755)
* feat: use hidRpcChannel to save bandwidth

* chore: simplify handshake of hid rpc

* add logs

* chore: add timeout when writing to hid endpoints

* fix issues

* chore: show hid rpc version

* refactor hidrpc marshal / unmarshal

* add queues for keyboard / mouse event

* chore: change logging level of JSONRPC send event to trace

* minor changes related to logging

* fix: nil check

* chore: add comments and remove unused code

* add useMouse

* chore: log msg data only when debug or trace mode

* chore: make tslint happy

* chore: unlock keyboardStateLock before calling onKeysDownChange

* chore: remove keyPressReportApiAvailable

* chore: change version handle

* chore: clean up unused functions

* remove comments
2025-09-04 22:27:56 +02:00
Marc Brooks 3ec243255b Add ability to track modifier state on the device (#725)
Remove LED sync source option and add keypress reporting while still working with devices that haven't been upgraded
We return the modifiers as the valid bitmask so that the VirtualKeyboard and InfoBar can represent the correct keys as down. This is important when we have strokes like Left-Control + Right-Control + Keypad-1 (used in switching KVMs and such).
Fix handling of modifier keys in client and also removed the extraneous resetKeyboardState.
Manage state to eliminate rerenders by judicious use of useMemo.
Centralized keyboard layout and localized display maps
Move keyboardOptions to useKeyboardLayouts
Added translations for display maps.
Add documentation on the legacy support.

Return the KeysDownState from keyboardReport
Clear out the hidErrorRollOver once sent to reset the keyboard to nothing down.
Handles the returned KeysDownState from keyboardReport
Now passes all logic through handleKeyPress.
If we get a state back from a keyboardReport, use it and also enable keypressReport because we now know it's an upgraded device.
Added exposition on isoCode management

Fix de-DE chars to reflect German E2 keyboard.
https://kbdlayout.info/kbdgre2/overview+virtualkeys

Ran go modernize
Morphs Interface{} to any
Ranges over SplitSeq and FieldSeq for iterating splits
Used min for end calculation remote_mount.Read
Used range 16 in wol.createMagicPacket
DID NOT apply the Omitempty cleanup.

Strong typed in the typescript realm.
Cleanup react state management to enable upgrading Zustand
2025-08-26 17:09:35 +02:00
Aveline 0c5c69f2d3 feat: sync keyboard led status (#502) 2025-05-23 00:12:18 +02:00
Aveline 189b84380b network enhanecment / refactor (#361)
* chore(network): improve connectivity check

* refactor(network): rewrite network and timesync component

* feat(display): show cloud connection status

* chore: change logging verbosity

* chore(websecure): update log message

* fix(ota): validate root certificate when downloading update

* feat(ui): add network settings tab

* fix(display): cloud connecting animation

* fix: golintci issues

* feat: add network settings tab

* feat(timesync): query servers in parallel

* refactor(network): move to internal/network package

* feat(timesync): add metrics

* refactor(log): move log to internal/logging package

* refactor(mdms): move mdns to internal/mdns package

* feat(developer): add pprof endpoint

* feat(logging): add a simple logging streaming endpoint

* fix(mdns): do not start mdns until network is up

* feat(network): allow users to update network settings from ui

* fix(network): handle errors when net.IPAddr is nil

* fix(mdns): scopedLogger SIGSEGV

* fix(dhcp): watch directory instead of file to catch fsnotify.Create event

* refactor(nbd): move platform-specific code to different files

* refactor(native): move platform-specific code to different files

* chore: fix linter issues

* chore(dev_deploy): allow to override PION_LOG_TRACE
2025-04-16 01:39:23 +02:00
Siyuan Miao 0ba7902f82 chore: update logging 2025-04-11 12:55:36 +02:00
Siyuan Miao 4c37f7e079 refactor: use structured logging 2025-04-11 00:43:46 +02:00
Aveline d52e7d04d1 feat: relative mouse (#246) 2025-03-19 11:47:15 +01:00
SuperQ 97ce785056 Fix more linter issues.
Signed-off-by: SuperQ <superq@gmail.com>
2025-03-11 18:35:12 +01:00
SuperQ 34e33e45bf Cleanup logging
Make sure all logging output is called via the main logger instead of
stdlib `"log"` or `fmt.Print(f|ln)`.

Signed-off-by: SuperQ <superq@gmail.com>
2025-03-11 16:53:54 +01:00
Siyuan Miao d1948adca8 refactor(usb): move usbconfig to a seperated package 2025-03-10 14:02:52 +01:00
Siyuan Miao c088534d34 feat(usb): dynamic usb devices config 2025-03-10 13:54:42 +01:00
Adam Shiervani 536e823243 feat: Add scroll sensitivity configuration and improved wheel event handling (#242)
- Implement scroll sensitivity settings with low, default, and high modes
- Add RPC methods for getting and setting scroll sensitivity
- Enhance wheel event handling with device-specific sensitivity and clamping
- Create a new device settings store for managing scroll and trackpad parameters
- Update mouse settings route to include scroll sensitivity selection
2025-03-10 10:51:11 +01:00
Aveline 554121a20b chore: ensure config is loaded before init functions (#226) 2025-03-04 11:32:54 +01:00
jackislanding 77263e73f7 Feature/usb config - Rebasing USB Config Changes on Dev Branch (#185)
* rebasing on dev branch

* fixed formatting

* fixed formatting

* removed query params

* moved usb settings to hardware setting

* swapped from error to log

* added fix for any change to product name now resulting in show the spinner as custom on page reload

* formatting

---------

Co-authored-by: JackTheRooster <adrian@rydeas.com>
Co-authored-by: Adam Shiervani <adam.shiervani@gmail.com>
2025-02-27 09:53:47 +01:00
Andrew Nicholson 63b3ef0151 Enable "Boot Interface Subclass" for keyboard and mouse. (#113)
This is often required for the keyboard/mouse to be recognized in
BIOS/UEFI firmware.
2025-02-12 15:08:03 +01:00
Adam Shiervani 20780b65db Release 202412292127 2024-12-29 21:27:42 +01:00