Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Six Axis HID #1092

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

Implement Six Axis HID #1092

wants to merge 2 commits into from

Conversation

Glodigit
Copy link
Contributor

@Glodigit Glodigit commented Mar 8, 2025

This is a chunk of #1087.
This PR contains the SpaceMouse Compact-compatible HID implementation and updated boot.md documentation.

@@ -93,7 +93,7 @@ def _send_hid(self) -> None:
self.hid_pending = False

for key in self.keys_pressed:
if isinstance(key, Axis):
if isinstance(key, Axis) or isinstance(key, SixAxis):
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure what this line accomplishes. I just added it to match with Axis.

Copy link
Collaborator

Choose a reason for hiding this comment

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

8bit axis can only move 127 units per report and we report movement > 127 in chunks. This part checks if there's no movement left and discards the axis from keys_pressed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see. For SixAxis, this would be values > 500 units.

Copy link
Collaborator

@xs5871 xs5871 left a comment

Choose a reason for hiding this comment

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

Is it required to have the sixaxis report be split into three?

@Glodigit
Copy link
Contributor Author

Are you referring to the 3 report IDs? Unfortunately, that's how 3dconnexion implemented it. There used to be 4 on devices predating the SpaceMouse Compact, but now they merge ID 1 and 2 into a single axis report on ID 1. I tried sending the 6 axis report on ID 2 and nothing moved, so it seems that 3DxWare expects specific reports on specific IDs.

I can only imagine these were all legacy decisions carried forward to simplify their long-term driver support.

@xs5871
Copy link
Collaborator

xs5871 commented Mar 11, 2025

A single ID would have simplified things. The conditional send_report is not a very nice solution. It has to run the check every time a report is send and 99% of users will never even use the sixaxis report.

@Glodigit
Copy link
Contributor Author

Glodigit commented Mar 11, 2025

That is true. What about something like:

def send(self):
    for report in self.device_map.keys():
        if report.pending:
            try:
                self.device_map[report].send_report(report.buffer)
            except ValueError:
                self.device_map[report].send_report(
                    report.buffer,
                    1 if len(report.buffer) == _REPORT_SIZE_SIXAXIS else 3)
            report.pending = False

@xs5871
Copy link
Collaborator

xs5871 commented Mar 12, 2025

No. We have to build seperability into it. It's planed to dynamically load specific report code based on available endpoints. KMK is too big and slow already.
The best I can come up with right now without refactoring the whole hid code is wrapping the send_report.

class DeviceReportId:
    def __init__(self, device, id):
        self.device = device
        self.id = id

    def send_report(self, buffer):
        self.decive.send_report(buffer, self.id)

# ...
class AbstractHID:
# ...
    def setup_sixaxis_hid(self):
        if device := find_device(self.devices, _USAGE_PAGE_SIXAXIS, _USAGE_SIXAXIS):
            report = SixAxisDeviceReport()
            self.report_map.update(report.get_action_map())
            self.device_map[report] = DeviceReportId(device, 1)
            report = SixAxisDeviceButtonReport()
            self.report_map.update(report.get_action_map())
            self.device_map[report] = DeviceReportId(device, 3)

@Glodigit
Copy link
Contributor Author

Since the ID is most likely under 255, what about having each buffer be 1 larger and report.buffer[0] (or [-1]) be the ID? Thus you send something like

self.device_map[report].send_report(report.buffer[1:], report.buffer[0])

Another issue is that the IDs need to be known ahead of time (e.g. keyboard might be ID 1 or ID 5).

@xs5871
Copy link
Collaborator

xs5871 commented Mar 12, 2025

My suggestion would only apply to the spacemouse emulation report. The report ID of keyboard and pointer doesn't matter, because they always are seperate endpoints with only one single report buffer. They don't need a wrapper around send_report.

The spacemouse is the exception here. Specifically the vendor specific host software that expects the report to be split into multiple report IDs. According to USB specs there's no reason why sixaxis and buttons, or any disimilar usage pages, couldn't be part of the same report. Every mouse with horizontal scrolling does that.

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.

2 participants