Skip to content

Add HID report protocol support for keyboards and mice, including scroll wheel support #675

Open
chrisgleissner wants to merge 15 commits intoGideonZ:masterfrom
chrisgleissner:feat/hid-mouse-keyboard
Open

Add HID report protocol support for keyboards and mice, including scroll wheel support #675
chrisgleissner wants to merge 15 commits intoGideonZ:masterfrom
chrisgleissner:feat/hid-mouse-keyboard

Conversation

@chrisgleissner
Copy link
Copy Markdown

@chrisgleissner chrisgleissner commented Mar 31, 2026

Hi Gideon,

I reworked the earlier PR so it no longer depends on any specific mouse model. It now targets standard HID reporting devices in general, and also adds keyboard support.

Features

The main features are:

  • HID reporting mouse and keyboard support
  • Fallback to HID boot mouse / keyboard when no HID reporting mouse / keyboard is available
  • Mouse support in the menu:
    • wheel moves the selection
    • left button activates / selects
    • right button acts as RUN/STOP / Back
  • Configurable mouse wheel speed and direction
  • Updated help text and device visibility/status reporting
  • Host-side tests for HID parsing, boot/report handling, keyboard queueing, and menu wheel behavior

Demo

U64 Elite-I with Logitech MX Master 3 (wireless via Logitech Unifying receiver) and Goldtouch USB keyboard:

https://youtu.be/QtKnri4DVqY

The video shows:

  • Control cursor movements with mouse wheels (vertical/horizontal)
  • Control mouse movements with mouse wheels (good for precise movements in paint programs etc.)
  • Extensions to the Joystick menu

Joystick Menu

JoystickMenu

Implementation Notes

  • I tried to keep the implementation as self-contained and consistent with the existing code base as possible, and I tested it quite a lot.
  • The extra menu settings are there because users tend to have very different expectations for mouse wheel speed and orientation.
  • If you would prefer fewer menu items, I can simplify that part and use fixed defaults instead, although it is difficult to pick values that feel right for everyone.

HID Mouse And Keyboard Flow

This is a high-level overview of the runtime flow added by the HID mouse and keyboard work.

The important behavior is:

  • A USB HID interface is used as a report-protocol mouse and/or keyboard when its report descriptor exposes standard mouse or keyboard fields.
  • If no active report-protocol sibling already provides that function, a boot-protocol mouse or keyboard is used as fallback.
  • Keyboard input is normalized to the existing boot-style 8-byte keyboard report, merged across active USB keyboard interfaces, and then fed into the existing Keyboard_USB path.
  • Mouse motion always updates the normal machine-side mouse state. When menu mouse navigation is enabled and a menu is open, wheel movement and the left/right mouse buttons also generate menu navigation keys.
  • The USB HID layer keeps the visible mouse/keyboard name and mode strings. The config menu snapshots that state on entry and refreshes the visible items when the HID status generation changes.

Further details:

flowchart TD
    A[USB HID interface discovered] --> B[Inspect interface capabilities]
    B --> C{Standard HID report fields found?}

    C -->|Yes| D[Activate report-protocol mouse and or keyboard]
    C -->|No| E{Boot mouse or keyboard available\nand no active report sibling?}
    E -->|Yes| F[Activate boot-protocol fallback]
    E -->|No| G[Ignore interface for HID input]

    D --> H[Receive interrupt reports]
    F --> H

    H --> I{Report type}

    I -->|Keyboard| J[Normalize to boot-style 8-byte report]
    J --> K[Merge all active USB keyboard sources]
    K --> L[Feed existing Keyboard_USB path]

    I -->|Mouse| M[Decode motion, buttons and wheel]
    M --> N[Update machine-side mouse state]
    M --> O{Menu active and\nmenu mouse navigation enabled?}
    O -->|Yes| P[Map wheel to navigation keys\nand left or right button to menu actions]
    O -->|No| Q[No additional menu actions]

    D --> R[Update HID name and mode snapshot]
    F --> R
    R --> S[Bump HID status generation]
    S --> T[Config menu refreshes visible HID status]
Loading

It would be great if you would consider merging it.

Thanks!

…enu navigation methods, update tests, and improve visibility handling
- Fixed the normalization of vertical wheel direction in HidMouseInterpreterTest.
- Added active_configuration member to UsbDevice to track the current configuration.
- Improved configuration handling in UsbDevice, preventing unnecessary configuration changes.
- Enhanced keyboard source management in usb_hid, allowing for better tracking and reporting of keyboard inputs.
- Updated interrupt handling in UsbHidDriver to correctly manage mouse and keyboard visibility.
- Refactored USB Hub driver to clean up code and improve readability.
- Introduced active_config_browser in ConfigBrowser to manage active configuration states and refresh UI accordingly.
- General code cleanup, including removal of unnecessary whitespace and comments.
… injected key counting, and update tests for improved behavior
…and update user interface help text for mouse controls
…nd update related tests for improved accuracy
Copilot AI review requested due to automatic review settings March 31, 2026 18:59
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR expands the USB HID stack to support standards-based HID report protocol devices (mouse + keyboard), with a boot-protocol fallback, and integrates mouse-wheel-driven navigation into the UI/menu system. It also adds new configuration/status items and host-side tests to validate HID parsing and behavior.

Changes:

  • Add HID report-descriptor parsing for mice/keyboards (including vertical + horizontal wheel) and synthesize boot keyboard reports for non-boot descriptors.
  • Extend UI/config to show connected USB mouse/keyboard visibility + HID mode and add wheel/menu navigation settings.
  • Add host-side USB/HID unit tests plus a Linux helper script for capturing wheel behavior.

Reviewed changes

Copilot reviewed 22 out of 24 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
software/userinterface/userinterface.h Exposes UserInterface::anyMenuActive() for menu-override behavior.
software/userinterface/userinterface.cc Tracks UI instances and extends help text for USB mouse menu controls.
software/userinterface/config_menu.h Adds deinit() and a static refresh hook for the active config browser.
software/userinterface/config_menu.cc Implements active ConfigBrowser tracking and refresh trigger.
software/u64/u64_config.h Adds on_edit() hook to update dynamic HID status items.
software/u64/u64_config.cc Adds HID-related config items, exposes config getter for USB HID, and publishes HID visibility into the config menu.
software/io/usb/usb_hub.h Whitespace-only cleanup.
software/io/usb/usb_hub.cc Whitespace/indentation cleanup in IRQ handling.
software/io/usb/usb_hid.h Extends HID driver state to support report parsing, wheel/menu navigation, and visibility reporting.
software/io/usb/usb_hid.cc Major rework: report/boot handling, wheel scaling, menu override logic, keyboard source merging, and visibility publishing.
software/io/usb/usb_device.h Tracks active configuration in UsbDevice.
software/io/usb/usb_device.cc Makes set_configuration() idempotent and adjusts set_interface() behavior.
software/io/usb/tests/usb_keyboard_queue_test.cpp Adds host-side tests for injected key queue behavior.
software/io/usb/tests/usb_hid_mouse_test.cpp Adds host-side tests for HID mouse parsing + wheel/menu scaling logic.
software/io/usb/tests/usb_hid_keyboard_test.cpp Adds host-side tests for HID keyboard parsing and composite-report handling.
software/io/usb/tests/task.h Stub header to build tests without RTOS task API.
software/io/usb/tests/Makefile Simple build/run targets for host-side tests.
software/io/usb/tests/host_test/host_test.h Minimal unit test framework for host-side C++ tests.
software/io/usb/tests/host_test_main.cpp Test runner main for the host-side framework.
software/io/usb/tests/FreeRTOS.h Stub header to build tests without RTOS headers.
software/io/usb/keyboard_usb.h Expands keyboard buffering and adds injected-key APIs for menu navigation.
software/io/usb/keyboard_usb.cc Implements injected key queue + matrix overlay behavior and safer keymap lookups.
software/io/usb/hid_decoder.h Extends HID parsing (counts, IDs, truncated descriptors) and adds helpers for boot synthesis + wheel/menu interpretation.
python/mouse_wheel_capture.py Adds a Linux tool to capture wheel event streams for tuning/analysis.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@GideonZ
Copy link
Copy Markdown
Owner

GideonZ commented Mar 31, 2026

Hi Christian! This is really AWESOME work! I am totally stunned that you were able to produce so much code in so little time. Did you get help with AI?

I don't understand all the changes yet. In particular, what I don't understand is the contamination with the user interface. Could you explain me why the user interface needs to know about the user interface, or vice versa? Does it have somethign to do with the focus? Like: where do the mouse movements go to? And if so, why is this needed for the mouse, but not for the keyboard? (it wasn't necessary, but apparently now it is..)

And.. it doesn't build for RiscV... Apparently there is now some code that needs getpid, or kill. I think you accidentally pulled in some libary stuff that is not yet natively supported. I still have to figure out where, though.

@chrisgleissner
Copy link
Copy Markdown
Author

chrisgleissner commented Apr 1, 2026

Hi Gideon,

Thanks a lot for your positive feedback. Much appreciated. I have further refined the code and addressed the review comments. Here are some details:

  • The HID status display path has been simplified. The USB task no longer reaches into ConfigBrowser or ConfigStore state at all. The joystick menu now snapshots the current mouse and keyboard names only when the page is entered again, which matches the existing "active IP address" style of update and removes the extra UI coordination.
  • The only remaining UI coupling is the menu-active check for mouse navigation. That coupling is still required because the mouse otherwise drives joystick / paddle output, so it needs an explicit gate to decide whether motion and buttons should go to the running machine or be translated into menu actions. The keyboard does not need the same routing decision because its events already flow through the keyboard queue consumed by the UI thread.
  • The RISC-V build failure is fixed. The missing newlib syscall hooks were added in the actual linked RISC-V objects, and I reproduced the PR build locally with the repository container image using ghcr.io/gideonz/riscv:latest and make u64ii. That now completes successfully, including the update image that was previously failing on _kill / _getpid.

And yes, I did leverage some AI help for these changes, specifically GPT 5.4 and Claude Opus 4.6. I find it's become very useful last year.

Thanks
Christian

@GideonZ
Copy link
Copy Markdown
Owner

GideonZ commented Apr 1, 2026

Sounds good!

I would like to understand tho where the getpid and kill functions were needed. I am concerned that we pulled in a library (-function) that is not supposed to be pulled in.

@chrisgleissner
Copy link
Copy Markdown
Author

I did some digging and the use of _getpid and _kill may have been related to the introduction of snprintf in usb_hid.cc. Not entirely sure though as I am having some difficulties with a full build locally, though your excellent Docker image helps a lot.

Either way, I am in the process of removing that call and pushing the various other simplifications I mentioned earlier - as soon as I have done a sanity check on my Ultimate 64 Elite. Should not take too long.

@chrisgleissner
Copy link
Copy Markdown
Author

chrisgleissner commented Apr 1, 2026

Hi Gideon,

I investigated the getpid / kill issue further. The root cause appears to be the formatted status display pulling in additional newlib stdio code, so I have removed that call.

On the UI side, the only remaining coupling (it's a feature, not a bug... :-) ) is mouse routing: when the menu is open, wheel and buttons control the menu; otherwise input follows the normal path. The keyboard already uses the existing queue and does not require this gating.

I retested the full PR on my local U64 Elite. All previously demonstrated tests pass, along with additional checks including live keyboard and mouse reconnect with immediate menu updates, and general keyboard functionality.

Note on the Joystick menu: it shows the most recently active device, either by connection or latest input. For example, if a keyboard exposing both keyboard and mouse interfaces is connected while the menu is open, it may appear under both until distinct input is received. Updates are immediate and do not require reopening the menu.

I added a new section to the PR overview at the top of this page titled "HID Mouse And Keyboard Flow". I hope that helps with the PR review.

Thanks again for reviewing and for the positive feedback. I may be less available for the rest of the week due to travel, but please let me know if anything else needs attention.

All the best,
Christian

@chrisgleissner
Copy link
Copy Markdown
Author

chrisgleissner commented Apr 2, 2026

Hi @GideonZ ,

I successfully built the u64, ue2, and u2r targets locally. I understand the latter two use the RISC-V CPU. So the issue that failed the CI build should be resolved now:

-rwxr-xr-x  1 chris chris 1.7M Apr  2 02:33 update_v3.14d-24-g71877787.u2r
-rw-r--r--  1 chris chris 5.0M Apr  2 02:23 update_v3.14d-24-g71877787.u64
-rw-r--r--  1 chris chris 4.4M Apr  2 02:24 update_v3.14d-24-g71877787.ue2

The u2r firmware size above is quite small, but maybe it is normal. Also, as I only have an Ultimate 64 Elite and don't have a JTAG transient way of reflashing my C64U safely with the ue2 firmware, I was able to test only the u64 firmware.

Thanks for approving the workflow so it can rebuild on CI.

Best,
Christian

@GideonZ
Copy link
Copy Markdown
Owner

GideonZ commented Apr 4, 2026

Hi Christian,

I hope to have some time this weekend to dig through your pull request and merge it. I might come back with some questions. :)

Best regards,
Gideon

@chrisgleissner
Copy link
Copy Markdown
Author

Hi @GideonZ ,

Thanks a lot for your interest, it is much appreciated. If you'd like me to change any of it, just let me know.

Happy Easter!
Christian

@chrisgleissner
Copy link
Copy Markdown
Author

Just a quick update:

  • Another owner of the Ultimate 64 Elite has successfully tested this firmware patch on his device (installed via official firmware installer) and confirmed that The Mouse by Retro Games (which did not work at all with firmware 3.14d) now works well.
  • I've also installed it via the official firmware installer on my Ultimate 64 Elite and could not find any issues with further testing, having been using it now for a few days.

- Introduce adaptive mouse acceleration options in the HID configuration.
- Implement functions for resolving and scaling pointer sensitivity.
- Update mouse sensitivity handling in the USB HID driver.
- Add tests for new mouse acceleration features and ensure proper configuration.
@chrisgleissner
Copy link
Copy Markdown
Author

Hi @GideonZ ,

I hope you are well.

Earlier today, the version 1.1 of the C64U firmware got released. There are a lot of test reports and articles about it. A few people mentioned that their mice were not working properly, or were not getting recognized. It would be great if this PR could get merged so more people can benefit from its improved mouse support.

I've implemented a few additional improvements:

  • Improved UX with separators between related sections and consistent terminology: We now refer to mouse pointer / mouse wheel speed-up factors consistency using the simpler term "Sensitivity".
  • Fixed "jittery" mouse movement (and issue in the official 3.14d firmware) when moving the mouse quickly.
  • Added Mouse Sensitivity with values Auto, 1, ..., 16
  • Added Mouse Acceleration with values Off, Adaptive. This is useful for having more control over precise small movements whilst ensuring fast movements allow you to quickly navigate across the entire screen.

The following screenshot shows all changes:
MouseExtensions

I have not re-recorded the demo video since the new features are pretty self-explanatory.

I've successfully built the U64 and UE2 firmwares, and I tested this via JTAG deployment to my Ultimate 64 Elite.

All the best,
Christian

@xlar54
Copy link
Copy Markdown
Contributor

xlar54 commented Apr 8, 2026 via email

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.

4 participants