UNPKG

dualsense-ts

Version:

A natural interface for your DualSense controller, with Typescript

277 lines (254 loc) โ€ข 8.75 kB
import { Momentary, Dpad, DpadParams, Mute, Unisense, UnisenseParams, Touchpad, Gyroscope, GyroscopeParams, Accelerometer, AccelerometerParams, } from "./elements"; import { Input, InputSet, InputParams } from "./input"; import { DualsenseHIDState, DualsenseHID, PlatformHIDProvider, InputId, } from "./hid"; import { Intensity } from "./math"; /** Settings for your Dualsense controller and each of its inputs */ export interface DualsenseParams extends InputParams { /** Sets the source for HID events. Default: decide automatically */ hid?: DualsenseHID | null; /** Settings for the Playstation button */ ps?: InputParams; /** Settings for the mute button */ mute?: InputParams; /** Settings for the options button */ options?: InputParams; /** Settings for the create button */ create?: InputParams; /** Settings for the triangle button */ triangle?: InputParams; /** Settings for the circle button */ circle?: InputParams; /** Settings for the cross button */ cross?: InputParams; /** Settings for the square button */ square?: InputParams; /** Settings for the dpad buttons */ dpad?: DpadParams; /** Settings for inputs on the left half of the controller */ left?: UnisenseParams; /** Settings for inputs on the right side of the controller */ right?: UnisenseParams; /** Settings for the touchpad inputs */ touchpad?: InputParams; /** Settings for the gyroscope */ gyroscope?: GyroscopeParams; /** Settings for the accelerometer */ accelerometer?: AccelerometerParams; } /** Represents a Dualsense controller */ export class Dualsense extends Input<Dualsense> { public readonly state: Dualsense = this; /** The Playstation button */ public readonly ps: Momentary; /** The mute button and status light */ public readonly mute: Mute; /** The options button */ public readonly options: Momentary; /** The create button */ public readonly create: Momentary; /** The triangle button */ public readonly triangle: Momentary; /** The circle button */ public readonly circle: Momentary; /** The cross, or X button */ public readonly cross: Momentary; /** The square button */ public readonly square: Momentary; /** The up/down/left/right dpad buttons */ public readonly dpad: Dpad; /** Inputs on the left half of the controller */ public readonly left: Unisense; /** Inputs on the right half of the controller */ public readonly right: Unisense; /** The touchpad; works like a pair of analog sticks */ public readonly touchpad: Touchpad; /** Tracks the controller's angular velocity */ public readonly gyroscope: Gyroscope; /** Tracks the controller's linear acceleration */ public readonly accelerometer: Accelerometer; /** Represents the underlying HID device. Provides input events */ public readonly hid: DualsenseHID; /** A virtual button representing whether or not a controller is connected */ public readonly connection: Momentary; public get active(): boolean { return Object.values(this).some( (input) => input !== this && input instanceof Input && input.active ); } constructor(params: DualsenseParams = {}) { super(params); this.ps = new Momentary({ icon: "ใŽฐ", name: "Home", ...(params.ps ?? {}), }); this.mute = new Mute({ icon: "๐Ÿ•ฉ", name: "Mute", ...(params.mute ?? {}), }); this.options = new Momentary({ icon: "โ‹ฏ", name: "Options", ...(params.options ?? {}), }); this.create = new Momentary({ icon: "๐Ÿ–‰", name: "Create", ...(params.create ?? {}), }); this.triangle = new Momentary({ icon: "๐ŸŸ•", name: "Triangle", ...(params.triangle ?? {}), }); this.circle = new Momentary({ icon: "โŠš", name: "Circle", ...(params.circle ?? {}), }); this.cross = new Momentary({ icon: "โฎฟ", name: "Cross", ...(params.cross ?? {}), }); this.square = new Momentary({ icon: "๐ŸŸ—", name: "Square", ...(params.square ?? {}), }); this.dpad = new Dpad({ icon: "D", name: "D-pad", ...(params.dpad ?? {}), }); this.left = new Unisense({ icon: "L", name: "Left", ...(params.left ?? {}), }); this.right = new Unisense({ icon: "R", name: "Right", ...(params.right ?? {}), }); this.touchpad = new Touchpad({ icon: "โŽš", name: "Touchpad", ...(params.touchpad ?? {}), }); this.connection = new Momentary({ icon: "๐Ÿ”—", name: "Connected", ...(params.square ?? {}), }); this.gyroscope = new Gyroscope({ icon: "โˆž", name: "Gyroscope", threshold: 0.01, ...(params.gyroscope ?? {}), }); this.accelerometer = new Accelerometer({ icon: "โคฒ", name: "Accelerometer", threshold: 0.01, ...(params.accelerometer ?? {}), }); this.connection[InputSet](false); this.hid = params.hid ?? new DualsenseHID(new PlatformHIDProvider()); this.hid.register((state: DualsenseHIDState) => { this.processHID(state); }); /** Refresh connection state */ setInterval(() => { const { provider: { connected }, } = this.hid; this.connection[InputSet](connected); if (!connected) this.hid.provider.connect(); }, 200); /** Refresh rumble state */ const rumbleMemo = { left: -1, right: -1 }; setInterval(() => { const left = this.left.rumble(); const right = this.right.rumble(); if ( this.connection.active && (left !== rumbleMemo.left || right !== rumbleMemo.right) ) { this.hid.setRumble(left * 255, right * 255); rumbleMemo.left = left; rumbleMemo.right = right; } }, 1000 / 30); } private get rumbleIntensity(): number { return (this.left.rumble() + this.right.rumble()) / 2; } /** Check or adjust rumble intensity evenly across both sides of the controller */ public rumble(intensity?: Intensity): number { this.left.rumble(intensity); this.right.rumble(intensity); return this.rumbleIntensity; } /** Distributes HID event values to the controller's inputs */ private processHID(state: DualsenseHIDState): void { this.ps[InputSet](state[InputId.Playstation]); this.options[InputSet](state[InputId.Options]); this.create[InputSet](state[InputId.Create]); this.mute[InputSet](state[InputId.Mute]); this.mute.status[InputSet](state[InputId.Status]); this.triangle[InputSet](state[InputId.Triangle]); this.circle[InputSet](state[InputId.Circle]); this.cross[InputSet](state[InputId.Cross]); this.square[InputSet](state[InputId.Square]); this.dpad.up[InputSet](state[InputId.Up]); this.dpad.down[InputSet](state[InputId.Down]); this.dpad.right[InputSet](state[InputId.Right]); this.dpad.left[InputSet](state[InputId.Left]); this.touchpad.button[InputSet](state[InputId.TouchButton]); this.touchpad.left.x[InputSet](state[InputId.TouchX0]); this.touchpad.left.y[InputSet](state[InputId.TouchY0]); this.touchpad.left.contact[InputSet](state[InputId.TouchContact0]); this.touchpad.left.tracker[InputSet](state[InputId.TouchId0]); this.touchpad.right.x[InputSet](state[InputId.TouchX1]); this.touchpad.right.y[InputSet](state[InputId.TouchY1]); this.touchpad.right.contact[InputSet](state[InputId.TouchContact1]); this.touchpad.right.tracker[InputSet](state[InputId.TouchId1]); this.left.analog.x[InputSet](state[InputId.LeftAnalogX]); this.left.analog.y[InputSet](state[InputId.LeftAnalogY]); this.left.analog.button[InputSet](state[InputId.LeftAnalogButton]); this.left.bumper[InputSet](state[InputId.LeftBumper]); this.left.trigger[InputSet](state[InputId.LeftTrigger]); this.left.trigger.button[InputSet](state[InputId.LeftTriggerButton]); this.right.analog.x[InputSet](state[InputId.RightAnalogX]); this.right.analog.y[InputSet](state[InputId.RightAnalogY]); this.right.analog.button[InputSet](state[InputId.RightAnalogButton]); this.right.bumper[InputSet](state[InputId.RightBumper]); this.right.trigger[InputSet](state[InputId.RightTrigger]); this.right.trigger.button[InputSet](state[InputId.RightTriggerButton]); this.gyroscope.x[InputSet](state[InputId.GyroX]); this.gyroscope.y[InputSet](state[InputId.GyroY]); this.gyroscope.z[InputSet](state[InputId.GyroZ]); this.accelerometer.x[InputSet](state[InputId.AccelX]); this.accelerometer.y[InputSet](state[InputId.AccelY]); this.accelerometer.z[InputSet](state[InputId.AccelZ]); } }