Skip to content

feat(webrtc): accept keyboard and mouse on an input DataChannel - #5

Merged
mlopezsegura merged 1 commit into
masterfrom
feature/webrtc-keyboard-mouse
Nov 8, 2026
Merged

mlopezsegura merged 1 commit into
masterfrom
feature/webrtc-keyboard-mouse

Conversation

@mlopezsegura

Copy link
Copy Markdown
Collaborator

Description

WebRTC clients could only send gamepad input. This adds keyboard and mouse:

  • The server creates a third DataChannel, input (ordered, reliable). Clients send each keyboard, mouse motion
    (relative or absolute), mouse button and scroll event on it as the same binary Moonlight input packet a native
    Moonlight client sends.
  • is_keyboard_mouse_packet() accepts only those packet types at their exact size, with a header that agrees, so a
    client cannot inject controller packets that bypass the gamepad bridge and its slots. Accepted packets go to
    input::passthrough(), so they behave exactly as with Moonlight (key mapping, modifiers, keyboard/mouse
    settings, release on disconnect).
  • Packets are only forwarded while the stream runs, like controller input.
  • Clients that do not know the channel (the Tizen TV app, older web clients) close it; gamepad input is untouched.

The browser client using it is mlopezsegura/moonlight-webrtc-web (web/input.mjs).

Screenshot

Issues Fixed or Closed

Roadmap Issues

Testing

  • Unit tests: AcceptsOnlyKeyboardAndMousePackets (every accepted type, controller packets, wrong size, truncated,
    header mismatch) and ForwardsKeyboardAndMousePacketsWhileTheStreamIsActive. test_sunshine --gtest_filter=Webrtc*:
    55 passed (Windows, ucrt64 Release build).
  • tests/webrtc_e2e: the stand-in client now sends F15 down/up, a pointer move it undoes and a controller packet on
    the channel. Against an isolated test instance the debug log shows both key packets and both moves reaching
    libvirtualhid; the controller packet is dropped.
  • With the web client in headless Edge (pair, start, pointer lock, CDP key events, in-page pointer events, wheel,
    Ctrl+Alt+Shift+Z release): the server log shows each key, exact motion deltas, X1 button down/up, scroll ±120 and
    the release of held modifiers.
  • Not run: the full test suite and a manual session with physical keyboard and mouse.

Type of Change

  • feat: New feature (non-breaking change which adds functionality)
  • fix: Bug fix (non-breaking change which fixes an issue)
  • docs: Documentation only changes
  • style: Changes that do not affect the meaning of the code (white-space, formatting, missing semicolons, etc.)
  • refactor: Code change that neither fixes a bug nor adds a feature
  • perf: Code change that improves performance
  • test: Adding missing tests or correcting existing tests
  • build: Changes that affect the build system or external dependencies
  • ci: Changes to CI configuration files and scripts
  • chore: Other changes that don't modify src or test files
  • revert: Reverts a previous commit
  • BREAKING CHANGE: Introduces a breaking change (can be combined with any type above)

Checklist

  • Code follows the style guidelines of this project
  • Code has been self-reviewed
  • Code has been commented, particularly in hard-to-understand areas
  • Code docstring/documentation-blocks for new or existing methods/components have been added or updated
  • Tests have been added or updated for new or modified functionality, including regression tests for bug fixes

AI Usage

See our AI usage policy.

  • AI tools were used while preparing this PR

This pull request was generated by Claude Code, Claude Opus 5.5, high thinking level

🤖 Generated with Claude Code

Browser clients send keyboard, relative and absolute mouse motion, mouse
button and scroll events as Moonlight input packets on a new ordered,
reliable "input" DataChannel. Only those packet types, at their exact
size, are passed to Sunshine's input code, and only while the stream
runs, so a client cannot inject controller packets that bypass the
gamepad bridge. Clients that do not know the channel close it, which
leaves gamepad input untouched.

The end-to-end test sends F15 and a pointer move it undoes on the
channel, plus a controller packet that must be dropped.

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

Copy link
Copy Markdown
Collaborator Author

This pull request was generated by Claude Code, Claude Opus 5.5, high thinking level

@mlopezsegura
mlopezsegura merged commit 0286c65 into master Nov 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant