Files
PackControl/node_modules/node-hid/nodehid.js

246 lines
7.5 KiB
JavaScript

const EventEmitter = require("events").EventEmitter;
const util = require("util");
let driverType = null;
function setDriverType(type) {
driverType = type;
}
// lazy load the C++ binding
let binding = null;
function loadBinding() {
if (!binding) {
const options = require('./binding-options');
if (process.platform === "linux" && (!driverType || driverType === "hidraw")) {
options.name = 'HID_hidraw';
}
binding = require("pkg-prebuilds/bindings")(__dirname, options);
}
}
//This class is a wrapper for `binding.HID` class
function HID() {
// see issue #150 (enhancement, solves issue #149)
// throw an error for those who forget to instantiate, i.e. by "*new* HID.HID()"
// and who would otherwise be left trying to figure out why "self.on is not a function"
if (!new.target) {
throw new Error('HID() must be called with \'new\' operator');
}
//Inherit from EventEmitter
EventEmitter.call(this);
loadBinding();
/* We also want to inherit from `binding.HID`, but unfortunately,
it's not so easy for native Objects. For example, the
following won't work since `new` keyword isn't used:
`binding.HID.apply(this, arguments);`
So... we do this craziness instead...
*/
var thisPlusArgs = new Array(arguments.length + 1);
thisPlusArgs[0] = null;
for(var i = 0; i < arguments.length; i++)
thisPlusArgs[i + 1] = arguments[i];
this._raw = new (Function.prototype.bind.apply(binding.HID,
thisPlusArgs) )();
/* Now we have `this._raw` Object from which we need to
inherit. So, one solution is to simply copy all
prototype methods over to `this` and binding them to
`this._raw`
*/
for(var i in binding.HID.prototype)
this[i] = binding.HID.prototype[i].bind(this._raw);
/* We are now done inheriting from `binding.HID` and EventEmitter.
Now upon adding a new listener for "data" events, we start
polling the HID device using `read(...)`
See `resume()` for more details. */
this._paused = true;
var self = this;
self.on("newListener", function(eventName, listener) {
if(eventName == "data")
process.nextTick(self.resume.bind(self) );
});
}
//Inherit prototype methods
util.inherits(HID, EventEmitter);
//Don't inherit from `binding.HID`; that's done above already!
HID.prototype.close = function close() {
this._closing = true;
this.removeAllListeners();
if (this._paused) {
// Don't exit if a read is currently running
this._raw.close();
this._closed = true;
} else {
// Make sure the read is stopped ASAP
this._raw.readInterrupt();
}
};
//Pauses the reader, which stops "data" events from being emitted
HID.prototype.pause = function pause() {
this._paused = true;
this._raw.readInterrupt();
};
HID.prototype.read = function read(callback) {
if (this._closed) {
throw new Error('Unable to read from a closed HID device');
} else {
return this._raw.read(callback);
}
};
HID.prototype.resume = function resume() {
var self = this;
if(self._paused && self.listeners("data").length > 0)
{
//Start polling & reading loop
self._paused = false;
self.read(function readFunc(err, data) {
try {
if (self._closing) {
// Discard any data if we're closing
self._paused = true;
self._raw.close();
self._closed = true;
return
}
if(err)
{
//Emit error and pause reading
self._paused = true;
if(!self._closing)
self.emit("error", err);
//else ignore any errors if I'm closing the device
}
else
{
//If there are no "data" listeners, we pause
if(self.listeners("data").length <= 0)
self._paused = true;
//Keep reading if we aren't paused
if(!self._paused)
self.read(readFunc);
//Now emit the event
self.emit("data", data);
}
} catch (e) {
// Emit an error on the device instead of propagating to a c++ exception
setImmediate(() => {
if (!self._closing)
self.emit("error", e);
});
}
});
}
};
class HIDAsync extends EventEmitter {
constructor(raw) {
super()
if (!(raw instanceof binding.HIDAsync)) {
throw new Error(`HIDAsync cannot be constructed directly. Use HIDAsync.open() instead`)
}
this._raw = raw
/* Now we have `this._raw` Object from which we need to
inherit. So, one solution is to simply copy all
prototype methods over to `this` and binding them to
`this._raw`.
We explicitly wrap them in an async method, to ensure
that any thrown errors are promise rejections
*/
for (let i in this._raw) {
this[i] = async (...args) => this._raw[i](...args);
}
/* Now upon adding a new listener for "data" events, we start
the read thread executing. See `resume()` for more details.
*/
this.on("newListener", (eventName, listener) =>{
if(eventName == "data")
process.nextTick(this.resume.bind(this) );
});
this.on("removeListener", (eventName, listener) => {
if(eventName == "data" && this.listenerCount("data") == 0)
process.nextTick(this.pause.bind(this) );
})
}
static async open(...args) {
loadBinding();
const native = await binding.openAsyncHIDDevice(...args);
return new HIDAsync(native)
}
async close() {
this._closing = true;
await this._raw.close();
this.removeAllListeners();
this._closed = true;
}
//Pauses the reader, which stops "data" events from being emitted
pause() {
this._raw.readStop();
}
resume() {
if(this.listenerCount("data") > 0)
{
//Start polling & reading loop
this._raw.readStart((err, data) => {
try {
if (err) {
if(!this._closing)
this.emit("error", err);
//else ignore any errors if I'm closing the device
} else {
this.emit("data", data);
}
} catch (e) {
// Emit an error on the device instead of propagating to a c++ exception
setImmediate(() => {
if (!this._closing)
this.emit("error", e);
});
}
})
}
}
}
function showdevices() {
loadBinding();
return binding.devices.apply(HID,arguments);
}
function showdevicesAsync(...args) {
loadBinding();
return binding.devicesAsync(...args);
}
function getHidapiVersion() {
loadBinding();
return binding.hidapiVersion;
}
//Expose API
exports.HID = HID;
exports.HIDAsync = HIDAsync;
exports.devices = showdevices;
exports.devicesAsync = showdevicesAsync;
exports.setDriverType = setDriverType;
exports.getHidapiVersion = getHidapiVersion;