-
Notifications
You must be signed in to change notification settings - Fork 12
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
Add implementation of XKeysWatcher for browser environments under web package #105
Conversation
a327121
to
4382b8a
Compare
Full disclosure: this is completely untested. I'm hoping to be able to do some testing in the near future. |
would the https://developer.mozilla.org/en-US/docs/Web/API/HID/connect_event and https://developer.mozilla.org/en-US/docs/Web/API/HID/disconnect_event be usable for this instead of polling? |
Thanks for the suggestion. Did some testing on my end with an X-Keys 128. Findings:
The
Which suggests the events are only fired for devices which have been granted permission through a call to At least with my X-Keys device this suggests the Also, through testing the polling approach works quite well (have yet to validate the entire implementation). |
Thanks for this contribution! Since this is a work-in-progress, I'll mark this PR as a draft for now. I gave this an initial test in Chrome, these are my findings:
Do you know if it's possible to get a browser to remember past connected panels? If not, I don't really see the point of adding the XKeysWatcher for browsers, since it won't add any functionality anyway? Or do you have a use case I'm missing? |
this.pollingTimeout = (setTimeout as Window['setTimeout'])( | ||
async () => { | ||
try { | ||
await this.updateConnectedDevices() | ||
} catch (e) { | ||
console.error(e) | ||
} | ||
this.triggerUpdateConnectedDevices(false) | ||
}, | ||
immediate ? 0 : this.options?.pollingInterval ?? DEFAULT_POLLING_INTERVAL_MS | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You must not pass an async funtion to setTimeout, as a thrown error will result in an uncaught Error.
this.pollingTimeout = (setTimeout as Window['setTimeout'])( | |
async () => { | |
try { | |
await this.updateConnectedDevices() | |
} catch (e) { | |
console.error(e) | |
} | |
this.triggerUpdateConnectedDevices(false) | |
}, | |
immediate ? 0 : this.options?.pollingInterval ?? DEFAULT_POLLING_INTERVAL_MS | |
) | |
this.pollingTimeout = (setTimeout as Window['setTimeout'])( | |
() => { | |
this.updateConnectedDevices() | |
.then(() => { | |
this.triggerUpdateConnectedDevices(false) | |
}) | |
.catch((e) => { | |
this.emit('error', e) | |
}) | |
}, | |
immediate ? 0 : this.options?.pollingInterval ?? DEFAULT_POLLING_INTERVAL_MS | |
) |
Thanks for the feedback and review @nytamin .
This matches the testing I had done.
I believe it has to do with the XKeys devices (at least the ones you and I have tested) not having a serial number https://issues.chromium.org/issues/40625708:
And here is the log from chrome://device-log/:
It would seem this info would need to be set on the XKeys panels devices themselves... I am not sure whether this is achievable. Regarding use cases for this implementation, the two use cases I have for this are:
|
Oh, that's a good spot that I haven't noticed before. The disconnect event doesn't fire in the WebHID version, which makes it inconsistent to the node-version. I addressed this in a separate PR: #107
Yeah, unfortunately the xkeys panels do not have any serial numbers in their hardware.
Ah, yes that's a reasonable use case. I think, in order to make documentation easier and the code maintainable, that I'd like to refactor this so that most of the code is shared between the node and the webhid versions. I've started to look into the refactoring, so I'll open another PR in a little while with those changes. Note to self: Before merging, make sure to update the documentation to make it clear that the watcher won't work "out of the box" in a browser, and that it must be paired with a |
Sounds good to the above, and thanks for the patch in #107. Let me know if you would like me to make changes to this PR once the refactors are opened. |
#104
Largely based on the existing implementation in https://github.com/SuperFlyTV/xkeys/blob/838adffd0b24b9a749e131855ea5d934f1c6c086/packages/node/src/watcher.ts
Key differences:
HIDDevice
instance references (stored in aSet
), as opposed to thedevicePath
(since it is not available for web). Per the spec, these instances should remain stable across calls to the underlyingnavigator.hid.getDevices()
(see https://wicg.github.io/webhid/#dom-hid-getdevices "6." > https://wicg.github.io/webhid/#dfn-devices)._handleDeviceReconnected
,_handleDeviceDisconnected
. I was not sure if/why these calls were needed.setTimeout
callbacks calling each other, rather thansetInterval
. This lends itself to a slightly less verbose implementation, and has the added benefit of not flooding the event loop if the polling interval causes a timer rate higher than the system can handle.Missing features:
automaticUnitIdMode
I tried to keep the code stylistically similar: comments, names, etc. Also included the "dead" debug logging code for convenience.