Skip to content

Instantly share code, notes, and snippets.

@bathos
Created March 1, 2017 15:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bathos/5fc24181ef25c2967a9df3358ff41b4b to your computer and use it in GitHub Desktop.
Save bathos/5fc24181ef25c2967a9df3358ff41b4b to your computer and use it in GitHub Desktop.
hid.js
const binary = require('node-pre-gyp');
const binding = require(binary.find(require.resolve('node-hid/package.json')));
const { Duplex } = require('stream');
const SHARED_METHOD_KEYS = [
'getDeviceInfo',
'getFeatureReport',
'sendFeatureReport',
'setNonBlocking'
];
app.factory('HID', () => {
const hids = new WeakMap();
class HID extends Duplex {
constructor({ lossyMode=false, path, productId, vendorId }={}) {
app.util.assert(
path || (vendorId && productId),
'HID requires either path or both vendorId and productId'
);
// Internal HID binding object. This, too, looks like a little like a
// stream insofar as it has .read() and .close(). This object is not
// exposed.
const hid = path
? new binding.HID(path)
: new binding.HID(vendorId, productId);
// The interior of the `_read` function is this tail-callish handler. Note
// that the result of `this.push` is ignored.
const handleData = (err, data) => {
if (err) {
this.emit('error', err);
return;
}
this.push(data);
if (!this._muted && (!this.lossyMode || this.listenerCount('data'))) {
hid.read(handleData);
}
};
super({
read: () => {
if (!this._muted) {
hid.read(handleData);
}
},
write: (chunk, enc, done) => {
try {
hid.write(chunk);
done();
} catch (err) {
done(err);
}
}
});
this.lossyMode = Boolean(lossyMode);
if (this.lossyMode) {
this.mute();
this.on('newListener', eventKey => {
if (eventKey === 'data') {
this.unmute();
}
});
}
hids.set(this, hid);
}
static devices() {
return binding.devices();
}
close() {
this.end(); // writable side
this.destroy(); // readable side
hids.get(this).close(); // internal source
}
// Since `pause` is already a stream method, node-hid’s original pause
// functionality, which has a very different meaning, is instead represented
// here with `mute` and `unmute`:
mute() {
this._muted = true;
}
unmute() {
this._muted = false;
}
}
for (const key of SHARED_METHOD_KEYS) {
Reflect.defineProperty(HID.prototype, key, {
configurable: true,
enumerable: false,
value() {
return hids.get(this)[key](...arguments);
},
writable: true
});
}
return HID;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment