dualsense-ts
Version:
A natural interface for your DualSense controller, with Typescript
136 lines • 4.75 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebHIDProvider = void 0;
const hid_provider_1 = require("./hid_provider");
class WebHIDProvider extends hid_provider_1.HIDProvider {
constructor() {
super();
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!navigator.hid)
throw new Error("WebHID not supported by this browser");
navigator.hid.addEventListener("disconnect", ({ device }) => {
if (device === this.device) {
this.device = undefined;
this.disconnect();
}
});
navigator.hid.addEventListener("connect", ({ device }) => {
if (!this.device)
this.attach(device);
});
}
/**
* WebHID API doesn't indicate whether we are connected through the controller's
* USB or Bluetooth interface. The protocol is different depending on the connection
* type so we will try to detect it based on the collection information.
*/
detectConnectionType() {
this.wireless = undefined;
if (!this.device) {
return;
}
for (const c of this.device.collections) {
if (c.usagePage !== hid_provider_1.HIDProvider.usagePage ||
c.usage !== hid_provider_1.HIDProvider.usage) {
continue;
}
// Compute the maximum input report byte length and compare against known values.
const maxInputReportBytes = (c.inputReports ?? []).reduce((max, report) => {
return Math.max(max, (report.items ?? []).reduce((sum, item) => {
return sum + (item.reportSize ?? 0) * (item.reportCount ?? 0);
}, 0));
}, 0);
if (maxInputReportBytes == 504) {
this.wireless = false;
}
else if (maxInputReportBytes == 616) {
this.wireless = true;
}
}
}
attach(device) {
device
.open()
.then(() => {
this.device = device;
this.detectConnectionType();
// Enable accelerometer, gyro, touchpad
return this.device.receiveFeatureReport(0x05);
})
.then(() => {
if (!this.device)
throw Error("Controller disconnected before setup");
this.device.addEventListener("inputreport", ({ reportId, data }) => {
this.buffer = data;
this.onData(this.process({ reportId, buffer: data }));
});
})
.catch((err) => {
this.onError(err);
this.disconnect();
});
}
/**
* You need to get HID device permissions from an interactive
* component, like a button. This returns a callback for triggering
* the permissions request.
*/
getRequest() {
return () => navigator.hid
.requestDevice({
filters: [
{
vendorId: hid_provider_1.HIDProvider.vendorId,
productId: hid_provider_1.HIDProvider.productId,
usagePage: hid_provider_1.HIDProvider.usagePage,
usage: hid_provider_1.HIDProvider.usage,
},
],
})
.then((devices) => {
if (devices.length === 0) {
return this.onError(new Error(`No controllers available`));
}
this.attach(devices[0]);
})
.catch((err) => {
this.onError(err);
});
}
connect() {
// Nothing to be done.
}
get connected() {
return this.device !== undefined;
}
disconnect() {
if (this.device) {
this.device.close().finally(() => this.reset());
}
else {
this.reset();
}
}
async write(data) {
if (!this.device)
return;
return this.device.sendFeatureReport(0, data);
}
process({ reportId, buffer, }) {
// DataView does not report the first byte (the report id), we simulate it
const report = {
length: buffer.byteLength + 1,
readUint8(offset) {
return offset > 0 ? buffer.getUint8(offset - 1) : reportId;
},
readUint16LE(offset) {
return offset > 0
? buffer.getUint16(offset - 1, true)
: (reportId << 8) | buffer.getUint8(0);
},
};
return this.processReport(report);
}
}
exports.WebHIDProvider = WebHIDProvider;
//# sourceMappingURL=web_hid_provider.js.map