Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/xan105/node-xinput-ffi
Access native XInput functions as well as some helpers based around them.
https://github.com/xan105/node-xinput-ffi
ffi gamepad nodejs pid rumble vibrate vid windows xinput
Last synced: about 1 month ago
JSON representation
Access native XInput functions as well as some helpers based around them.
- Host: GitHub
- URL: https://github.com/xan105/node-xinput-ffi
- Owner: xan105
- License: mit
- Created: 2020-08-07T20:41:13.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2024-05-23T12:42:08.000Z (7 months ago)
- Last Synced: 2024-10-12T19:51:21.763Z (2 months ago)
- Topics: ffi, gamepad, nodejs, pid, rumble, vibrate, vid, windows, xinput
- Language: JavaScript
- Homepage:
- Size: 282 KB
- Stars: 6
- Watchers: 2
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
About
=====Access native XInput functions as well as some helpers based around them.
This lib hooks directly to the system's dll (xinput1_4.dll, xinput1_3.dll or xinput9_1_0.dll).
It aims to implement and expose XInput functions as close as possible to the document.🔍 "Hidden" XInput functions such as `XInputGetCapabilitiesEx()` are exposed as well.
Examples
========Vibration via helper function
```js
import { rumble } from "xinput-ffi";//Rumble 1st XInput gamepad
await rumble();//Now with 100% force
await rumble({force: 100});//low-frequency rumble motor(left) at 50%
//and high-frequency rumble motor (right) at 25%
await rumble({force: [50,25]});
```XInput function
```js
import * as XInput from "xinput-ffi";const capabilities = await XInput.getCapabilities({translate: true});
console.log(capabilities);
/* Output:
{
type: 'XINPUT_DEVTYPE_GAMEPAD',
subType: 'XINPUT_DEVSUBTYPE_GAMEPAD',
flags: [ 'XINPUT_CAPS_VOICE_SUPPORTED', 'XINPUT_CAPS_PMD_SUPPORTED' ],
gamepad: {
wButtons: [
'XINPUT_GAMEPAD_DPAD_UP',
'XINPUT_GAMEPAD_DPAD_DOWN',
//etc...
],
bLeftTrigger: 255,
bRightTrigger: 255,
sThumbLX: -64,
sThumbLY: -64,
sThumbRX: -64,
sThumbRY: -64
},
vibration: { wLeftMotorSpeed: 255, wRightMotorSpeed: 255 }
}
*/
```"Hidden" XInput function
```js
import * as XInput from "xinput-ffi";const state = await XInput.getStateEx();
console.log(state);
/*Output:
{
dwPacketNumber: 6510,
gamepad: {
wButtons: [ 'XINPUT_GAMEPAD_GUIDE' ],
bLeftTrigger: 0,
bRightTrigger: 0,
sThumbLX: -1024,
sThumbLY: 767,
sThumbRX: 257,
sThumbRY: 767
}
}
*/
```Miscellaneous
```js
import * as XInput from "xinput-ffi";//Check connected status for all controller
console.log(await XInput.listConnected());
// [true,false,false,false] Only 1st gamepad is connected
//Identify connected XInput devices
console.log (await XInput.identify({XInputOnly: true}));
/* Output:
[
{
name: 'Xbox360 Controller',
manufacturer: 'Microsoft Corp.',
vendorID: 1118,
productID: 654,
xinput: true,
interfaces: [ 'USB', 'HID' ],
guid: [
'{745a17a0-74d3-11d0-b6fe-00a0c90f57da}',
'{d61ca365-5af4-4486-998b-9db4734c6ca3}'
]
}
]
*/
```### Electron
Simple XInput menu navigation
Here is an example of a simple XInput menu navigation system using the high level XInput implementation found in this module (helper).+ main process
```js
let gamepad;
mainWin.once("ready-to-show", async() => {
const { XInputGamepad } = await import("xinput-ffi");
gamepad = new XInputGamepad();
//send input to renderer
gamepad.on("input", (buttons)=>{
setImmediate(() => {
mainWin.webContents.send("onGamepadInput", buttons);
});
});
gamepad.poll(); //gamepad event loop
mainWin.show();
mainWin.focus();
});//gain/loose focus
mainWin.on("blur", () => {
gamepad?.pause();
});
mainWin.on("focus", () => {
gamepad?.resume();
});//clean up
mainWin.on("close", () => {
gamepad?.stop();
gamepad = null; //deref
});mainWin.on("closed", () => {
mainWin = null; //deref
});mainWin.loadFile(path/to/file);
```+ contextBridge (preload)
```js
contextBridge.exposeInMainWorld("ipcRenderer", {
onGamepadInput: (callback) => ipcRenderer.on("onGamepadInput", callback)
});
```+ renderer
```js
window.ipcRenderer.onGamepadInput((event, input) => {
switch(input[0]){
case "XINPUT_GAMEPAD_DPAD_UP":
//do something
break;
default:
console.log(input);
}
});
```Installation
============```
npm install xinput-ffi
```API
===⚠️ This module is only available as an ECMAScript module (ESM) starting with version 2.0.0.
Previous version(s) are CommonJS (CJS) with an ESM wrapper.## Named export
Summary:
- [constants](#const-constants--object)
- [XInput function](#xinput-function)
- [Helper functions](#helper-functions)
- [Identify device | VID/PID](#identify-device--vidpid)
- [High level implementation of XInput](#high-level-implementation-of-xinput)### `const constants = object`
XInput controller constants for convenience.
```js
import { constants } from "xinput-ffi";
console.log(constants.XUSER_MAX_COUNT); //4
```
💡 Also available under its own namespace.
```js
import { XUSER_MAX_COUNT } from "xinput-ffi/constants";
console.log(XUSER_MAX_COUNT); //4
```### XInput function
📖 [Microsoft documentation](https://docs.microsoft.com/en-us/windows/win32/xinput/functions)
- ✔️ [XInputEnable](https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputenable)
- ❌ [XInputGetAudioDeviceIds]() _> deprecated: doesn't work on modern Windows system._
- ✔️ [XInputGetBatteryInformation](https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputgetbatteryinformation)
- ✔️ [XInputGetCapabilities](https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputgetcapabilities)
- ❌ [XInputGetDSoundAudioDeviceGuids]() _> deprecated: doesn't work on modern Windows system._
- ✔️ [XInputGetKeystroke](https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputgetkeystroke)
- ✔️ [XInputGetState](https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputgetstate)
- ✔️ [XInputSetState](https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputsetstate)"Hidden" and undocumented functions
📖 [Reverse Engineer's log](https://reverseengineerlog.blogspot.com/2016/06/xinputs-hidden-functions.html)- ✔️ XInputGetStateEx
- ✔️ XInputWaitForGuideButton
- ✔️ XInputCancelGuideButtonWait
- ✔️ XInputPowerOffController
- ⚠️ XInputGetBaseBusInformation _> Not working with all gamepad._
- ✔️ XInputGetCapabilitiesExNB: Depending on which XInput dll version you are using *(1_4, 1_3, 9_1_0)* some functions won't be available.
#### `enable(enable: boolean): Promise`
Enable/Disable all XInput gamepads.
This function is meant to be called when an application gains or loses focus.NB:
- Stop any rumble currently playing when set to false.
- This may trigger `ERR_DEVICE_NOT_CONNECTED` for set/getState(Ex) when set to false and there was no prior input ever.
📖 [XInputEnable](https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputenable)
#### `getBatteryInformation(option?: number | object): Promise`Retrieves the battery type and charge status of a wireless controller.
⚙️ options:
- dwUserIndex?: number (0)
Index of the user's controller. Can be a value from 0 to 3.
- devType?: number (0)
Specifies which device associated with this controller should be queried.
0: GAMEPAD or 1: HEADSET- translate?: boolean (true)
When a value is known it will be 'translated' to its string equivalent value otherwise its integer value.
If you want the raw data only set it to false.💡 If `option` is a number it will be used as dwUserIndex.
Returns an object like a 📖 [XINPUT_BATTERY_INFORMATION](https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_battery_information) structure.
Example
```js
await getBatteryInformation();
await getBatteryInformation(0);
await getBatteryInformation({dwUserIndex: 0});
//output
{
batteryType: 'BATTERY_TYPE_WIRED',
batteryLevel: 'BATTERY_LEVEL_FULL'
}
```If you want raw data output
```js
await getBatteryInformation({translate: false});
//output
{
batteryType: 1,
batteryLevel: 3
}
```📖 [XInputGetBatteryInformation](https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputgetbatteryinformation)
#### `getCapabilities(option?: number | object): Promise`
Retrieves the capabilities and features of the specified controller.
⚙️ options:
- dwUserIndex?: number (0)
Index of the user's controller. Can be a value from 0 to 3.
- dwFlags?: number (1)
Input flags that identify the controller type.
If this value is 0, then the capabilities of all controllers connected to the system are returned.
Currently, only 1: XINPUT_FLAG_GAMEPAD is supported.- translate?: boolean (true)
When a value is known it will be 'translated' to its string equivalent value otherwise its integer value.
If you want the raw data only set it to false.💡 If `option` is a number it will be used as dwUserIndex.
Returns an object like a 📖 [XINPUT_CAPABILITIES](https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_capabilities) structure.
Example
```js
await getCapabilities();
await getCapabilities(0);
await getCapabilities({dwUserIndex: 0});
//Output
{
type: 'XINPUT_DEVTYPE_GAMEPAD',
subType: 'XINPUT_DEVSUBTYPE_GAMEPAD',
flags: [ 'XINPUT_CAPS_VOICE_SUPPORTED', 'XINPUT_CAPS_PMD_SUPPORTED' ],
gamepad: {
wButtons: [
'XINPUT_GAMEPAD_DPAD_UP',
'XINPUT_GAMEPAD_DPAD_DOWN',
'XINPUT_GAMEPAD_DPAD_LEFT',
'XINPUT_GAMEPAD_DPAD_RIGHT',
'XINPUT_GAMEPAD_START',
'XINPUT_GAMEPAD_BACK',
'XINPUT_GAMEPAD_LEFT_THUMB',
'XINPUT_GAMEPAD_RIGHT_THUMB',
'XINPUT_GAMEPAD_LEFT_SHOULDER',
'XINPUT_GAMEPAD_RIGHT_SHOULDER',
'XINPUT_GAMEPAD_A',
'XINPUT_GAMEPAD_B',
'XINPUT_GAMEPAD_X',
'XINPUT_GAMEPAD_Y'
],
bLeftTrigger: 255,
bRightTrigger: 255,
sThumbLX: -64,
sThumbLY: -64,
sThumbRX: -64,
sThumbRY: -64
},
vibration: { wLeftMotorSpeed: 255, wRightMotorSpeed: 255 }
}
```If you want raw data output
```js
await getCapabilities({translate: false});
//output
{
type: 1,
subType: 1,
flags: 12,
gamepad: {
wButtons: 65535,
bLeftTrigger: 255,
bRightTrigger: 255,
sThumbLX: -64,
sThumbLY: -64,
sThumbRX: -64,
sThumbRY: -64
},
vibration: { wLeftMotorSpeed: 255, wRightMotorSpeed: 255 }
}
```📖 [XInputGetCapabilities](https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputgetcapabilities)
#### `getKeystroke(option?: number | object): Promise`
Retrieves a gamepad input event.
To be honest, this isn't really useful since the chatpad feature wasn't implemented on Windows.
⚠️ NB: If no new keys have been pressed, this will throw with ERROR_EMPTY.⚙️ options:
- dwUserIndex?: number (0)
Index of the user's controller. Can be a value from 0 to 3.
- translate?: boolean (true)
When a value is known it will be 'translated' to its string equivalent value otherwise its integer value.
If you want the raw data only set it to false.💡 If `option` is a number it will be used as dwUserIndex.
Returns an object like a 📖 [XINPUT_KEYSTROKE](https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_keystroke) structure.
Example
```js
await getKeystroke();
await getKeystroke(0);
await getKeystroke({dwUserIndex: 0});
//Output
{
virtualKey: 'VK_PAD_A',
unicode: 0,
flags: [ 'XINPUT_KEYSTROKE_KEYDOWN' ],
userIndex: 0,
hidCode: 0
}
```If you want raw data output
```js
await getKeystroke({translate: false});
//output
{
virtualKey: 22528,
unicode: 0,
flags: 1,
userIndex: 0,
hidCode: 0
}
```📖 [XInputGetKeystroke](https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputgetkeystroke)
#### `getState(option?: number | object): Promise`
Retrieves the current state of the specified controller.
⚙️ options:
- dwUserIndex?: number (0)
Index of the user's controller. Can be a value from 0 to 3.
- translate?: boolean (true)
When a value is known it will be 'translated' to its string equivalent value otherwise its integer value.
If you want the raw data only set it to false.💡 If `option` is a number it will be used as dwUserIndex.
Returns an object like a 📖 [XINPUT_STATE](https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_state) structure.
Example
```js
await getState();
await getState(0);
await getState({dwUserIndex: 0});
//Output
{
dwPacketNumber: 18165,
gamepad: {
wButtons: ['XINPUT_GAMEPAD_A'],
bLeftTrigger: 0,
bRightTrigger: 0,
sThumbLX: 128,
sThumbLY: 641,
sThumbRX: -1156,
sThumbRY: -129
}
}
```If you want raw data output
```js
await getState({translate: false});
//output
{
dwPacketNumber: 322850,
gamepad: {
wButtons: 4096,
bLeftTrigger: 0,
bRightTrigger: 0,
sThumbLX: 257,
sThumbLY: 767,
sThumbRX: 773,
sThumbRY: 1279
}
}
```💡 Thumbsticks: as explained by Microsoft you should [implement dead zone correctly](https://docs.microsoft.com/en-us/windows/win32/xinput/getting-started-with-xinput#dead-zone).
This is done for you in [getButtonsDown()](https://github.com/xan105/node-xinput-ffi#getbuttonsdown-option-object-object)📖 [XInputGetState](https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputgetstate)
#### `setState(lowFrequency: number, highFrequency: number, option ?: number | object): Promise`
Sends data to a connected controller. This function is used to activate the vibration function of a controller.
⚙️ options:
- dwUserIndex?: number (0)
Index of the user's controller. Can be a value from 0 to 3.
- usePercent?: boolean (true)
`XInputSetState` valid values are in the range 0 to 65535.
Zero signifies no motor use; 65535 signifies 100 percent motor use.
`lowFrequency` and `highFrequency` are in % (0-100) for convenience when you set this to true.💡 If `option` is a number it will be used as dwUserIndex.
NB:
- You need to keep the event-loop alive otherwise the vibration will terminate with your program.
- You need to reset the state to 0 for both frequency before using setState again.Both are done for you with [rumble()](https://github.com/xan105/node-xinput-ffi#rumble-option-object-void)
📖 [XInputSetState](https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputsetstate)
#### `getStateEx(option?: number | object): Promise`
The same as `XInputGetState`, adding the "Guide" button (0x0400).
⚙️ options:
- dwUserIndex?: number (0)
Index of the user's controller. Can be a value from 0 to 3.
- translate?: boolean (true)
When a value is known it will be 'translated' to its string equivalent value otherwise its integer value.
If you want the raw data only set it to false.💡 If `option` is a number it will be used as dwUserIndex.
Returns an object like a 📖 [XINPUT_STATE](https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_state) structure.
Example
```js
await getStateEx();
await getStateEx(0);
await getStateEx({dwUserIndex: 0});
//Output
{
dwPacketNumber: 18165,
gamepad: {
wButtons: ['XINPUT_GAMEPAD_GUIDE'],
bLeftTrigger: 0,
bRightTrigger: 0,
sThumbLX: 128,
sThumbLY: 641,
sThumbRX: -1156,
sThumbRY: -129
}
}
```If you want raw data output
```js
await getStateEx({translate: false});
//output
{
dwPacketNumber: 322850,
gamepad: {
wButtons: 1024,
bLeftTrigger: 0,
bRightTrigger: 0,
sThumbLX: 257,
sThumbLY: 767,
sThumbRX: 773,
sThumbRY: 1279
}
}
```#### `waitForGuideButton(option?: number | object): Promise`
Wait until Guide button is pressed.
⚙️ options:
- dwUserIndex?: number (0)
Index of the user's controller. Can be a value from 0 to 3.
- dwFlags?: number (0)
Wait behavior:
0: Blocking 1: Async
It's not clear on how to get the async option to report.💡 If `option` is a number it will be used as dwUserIndex.
#### `cancelGuideButtonWait(option?: number | object): Promise`
If `XInputWaitForGuideButton` was activated in async mode, this will stop it.
⚙️ options:
- dwUserIndex?: number (0)
Index of the user's controller. Can be a value from 0 to 3.
💡 If `option` is a number it will be used as dwUserIndex.
#### `powerOffController(option?: number | object): Promise`
Power off a controller.
⚙️ options:
- dwUserIndex?: number (0)
Index of the user's controller. Can be a value from 0 to 3.
💡 If `option` is a number it will be used as dwUserIndex.
#### `getBaseBusInformation(option?: number | object): Promise`
⚠️ Not working on all gamepads. It can refuse and return `ERROR_DEVICE_NOT_CONNECTED`, even if connected.
⚙️ options:
- dwBusIndex?: number (0)
Bus index. Can be a value from 0 to 16.
💡 If `option` is a number it will be used as dwBusIndex?.
Returns an object like the following structure:
```c++
struct XINPUT_BASE_BUS_INFORMATION
{
WORD VendorId, //unknown
WORD ProductId, //unknown
WORD InputId, //unknown
WORD Field_6, //unknown
DWORD Field_8, //unknown
BYTE Field_C, //unknown
BYTE Field_D, //unknown
BYTE Field_E, //unknown
BYTE Field_F //unknown
}
```#### `getCapabilitiesEx(option?: number | object): Promise`
The same as `XInputGetCapabilities` but with added properties such as vendorID and productID.
⚙️ options:
- dwUserIndex?: number (0)
Index of the user's controller. Can be a value from 0 to 3.
- translate?: boolean (true)
When a value is known it will be 'translated' to its string equivalent value otherwise its integer value.
If you want the raw data only set it to false.💡 If `option` is a number it will be used as dwUserIndex.
Returns an object similar to 📖 [XINPUT_CAPABILITIES](https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_capabilities) structure.
See below for details.Example
```js
await getCapabilitiesEx();
await getCapabilitiesEx(0);
await getCapabilitiesEx({dwUserIndex: 0});
//Output
{
capabilities: {
type: 'XINPUT_DEVTYPE_GAMEPAD',
dubType: 'XINPUT_DEVSUBTYPE_GAMEPAD',
flags: [ 'XINPUT_CAPS_VOICE_SUPPORTED', 'XINPUT_CAPS_PMD_SUPPORTED' ],
gamepad: {
wButtons: [
'XINPUT_GAMEPAD_DPAD_UP',
'XINPUT_GAMEPAD_DPAD_DOWN',
'XINPUT_GAMEPAD_DPAD_LEFT',
'XINPUT_GAMEPAD_DPAD_RIGHT',
'XINPUT_GAMEPAD_START',
'XINPUT_GAMEPAD_BACK',
'XINPUT_GAMEPAD_LEFT_THUMB',
'XINPUT_GAMEPAD_RIGHT_THUMB',
'XINPUT_GAMEPAD_LEFT_SHOULDER',
'XINPUT_GAMEPAD_RIGHT_SHOULDER',
'XINPUT_GAMEPAD_A',
'XINPUT_GAMEPAD_B',
'XINPUT_GAMEPAD_X',
'XINPUT_GAMEPAD_Y'
],
bLeftTrigger: 255,
bRightTrigger: 255,
sThumbLX: -64,
sThumbLY: -64,
sThumbRX: -64,
sThumbRY: -64
},
vibration: { wLeftMotorSpeed: 255, wRightMotorSpeed: 255 }
},
vendorId: 'Microsoft Corp.',
productId: 'Xbox360 Controller',
productVersion: 276,
}
```If you want raw data output
```js
await getCapabilitiesEx({translate: false});
//output
{
capabilities: {
type: 1,
dubType: 1,
flags: 12,
gamepad: {
wButtons: 62463,
bLeftTrigger: 255,
bRightTrigger: 255,
sThumbLX: -64,
sThumbLY: -64,
sThumbRX: -64,
sThumbRY: -64
},
vibration: {
wLeftMotorSpeed: 255,
wRightMotorSpeed: 255
}
},
vendorId: 1118,
productId: 654,
productVersion: 276,
}
```### Helper functions
The following are sugar/helper functions based upon the previous XInput functions.
#### `isConnected(gamepad?: number): Promise`
Whether the specified controller is connected or not.
Returns true/false.#### `listConnected(): Promise`
Returns an array of connected status for all controller.
eg: [true,false,false,false] => Only 1st gamepad is connected#### `getButtonsDown(option?: object): Promise`
Normalize `getState()/getStateEx()` information for convenience:
ThumbStick position, magnitude, direction (taking the deadzone into account).
Trigger state and force (taking threshold into account).
Which buttons are pressed if any.⚙️ options:
- gamepad?: number (0)
Index of the user's controller. Can be a value from 0 to 3.
- deadzone?: number | number[] ( [7849,8689] )
Thumbstick deadzone(s):
Either an integer (both thumbstick with the same value) or an array of 2 integer: [left,right]
- directionThreshold?: number (0.2)float [0.0,1.0] to handle cardinal direction.
Set it to `0` so `direction[]` only reports "UP RIGHT", "UP LEFT", "DOWN LEFT", "DOWN RIGHT".
Otherwise "RIGHT", "LEFT", "UP", "DOWN" will be added to the above using threshold to
differentiate the 2 axes by using range of [-threshold,threshold].💡 If you **just** want "RIGHT", "LEFT", "UP" and "DOWN" the easiest way is to set this to `0.8` with the default deadzone.
Alternatively play with this value and/or deadzone to decide on a thresold and ignore when `direction[]` has a length of 2.
- triggerThreshold?: number (30)Trigger activation threshold. Range [0,255].
=> Returns an object where:
- int packetNumber : dwPacketNumber; This value is increased every time the state of the controller has changed.
- []string buttons : list of currently pressed [buttons](https://docs.microsoft.com/en-us/windows/win32/api/xinput/ns-xinput-xinput_gamepad#members)
- trigger.left/right :
+ boolean active : is the trigger pressed down ? (below triggerThreshold will not set active to true)
+ int force : by how much ? [0,255]
- thumb.left/right :
+ float x: normalized (deadzone) x axis [0.0,1.0]. 0 is centered. Negative values is left. Positive values is right.
+ float y: normalized (deadzone) y axis [0.0,1.0]. 0 is centered. Negative values is down. Positive values is up.
+ float magnitude: normalized (deadzone) magnitude [0.0,1.0] (by how far is the thumbstick from the center ? 1 is fully pushed).
+ []string direction: Human readable direction of the thumbstick. eg: ["UP", "RIGHT"]. See directionThreshold above for details.```js
{
packetNumber: 132309,
buttons: [ 'XINPUT_GAMEPAD_A' ],
trigger: {
left: { active: true, force: 255 },
right: { active: false, force: 0 }
},
thumb: {
left: {
x: -0.6960457056589758,
y: 0.717997476063599,
magnitude: 1,
direction: [ 'UP', 'LEFT' ]
},
right: {
x: 0.039307955814283674,
y: 0.9992271436513833,
magnitude: 1,
direction: [ 'UP' ]
}
}
}
```
#### `rumble(option?: object): Promise`This function is used to activate the vibration function of a controller.
⚙️ options:
- gamepad?: number (0)
Index of the user's controller. Can be a value from 0 to 3.
- force?: number | number[] ([50,25])
Vibration force in % (0-100) to apply to the motors.
Either an integer (both motor with the same value) or an array of 2 integer: [left,right]- duration?: number (2500)
Vibration duration in ms. Max: ~2500 ms.
- forceEnableGamepad?: boolean (false)
Use `enable()` to force the activation of XInput gamepad before vibration.
- forceStateWhileRumble?: boolean (false)Bruteforce _-ly_ (spam) `setState()` for the duration of the vibration. Use this when a 3rd party reset your state or whatever.
⚠️ Usage of this option is not recommended use only when needed.### Identify device | VID/PID
XInput doesn't provide VID/PID **by design**.
Even if with `XInputGetCapabilitiesEx` you can get the vendorID and productID, it will most likely be a Xbox Controller (real one or through XInput emulation).Use `identify()` (see below) to query `WMI _Win32_PNPEntity` to scan for known gamepads.
It won't tell you which is connected to which XInput slot tho.#### `identify(option?: object): Promise`
⚠️ Requires PowerShell.
List all **known** HID and USB connected devices **by matching with entries in** `./lib/util/HardwareID.js`
⚙️ options:
- XInputOnly?: boolean (true)
Return only XInput gamepad.
=> Return an array of object where
- string name : device name
- string manufacturer : vendor name
- number vendorID : vendor id
- number productID : product id
- string[] interfaces : PNPentity interface(s) found; Available: HID and USB
- string[] guid: classguid(s) found
- boolean xinput: a XInput device or not💡 object are unique by their vid/pid
Output example with a DS4(wireless) and ds4windows(XInput wrapper):
```js
import { identify } from "xinput-ffi";
await identify();
//Output
[
{
name: 'DualShock 4 (v2)',
manufacturer: 'Sony Corp.',
vendorID: 1356,
productID: 2508,
xinput: false,
interfaces: [ 'USB', 'HID' ],
guid: [
'{36fc9e60-c465-11cf-8056-444553540000}',
'{745a17a0-74d3-11d0-b6fe-00a0c90f57da}',
'{4d36e96c-e325-11ce-bfc1-08002be10318}'
]
},
{
name: 'DualShock 4 USB Wireless Adaptor',
manufacturer: 'Sony Corp.',
vendorID: 1356,
productID: 2976,
xinput: false,
interfaces: [ 'USB', 'HID' ],
guid: [
'{745a17a0-74d3-11d0-b6fe-00a0c90f57da}',
'{36fc9e60-c465-11cf-8056-444553540000}',
'{4d36e96c-e325-11ce-bfc1-08002be10318}'
]
},
{
name: 'Xbox360 Controller',
manufacturer: 'Microsoft Corp.',
vendorID: 1118,
productID: 654,
xinput: true,
interfaces: [ 'USB', 'HID' ],
guid: [
'{745a17a0-74d3-11d0-b6fe-00a0c90f57da}',
'{d61ca365-5af4-4486-998b-9db4734c6ca3}'
]
}
]
```### High level implementation of XInput
This is a high level implementation of XInput to get the gamepad's input on the fly in a human readable way.
This serves as an example to demonstrate how to use the XInput functions and helpers based around them.
The purpose of this class is to drive a simple navigation menu system with a XInput compatible controller (real XInput or through XInput emulation).This leverages the new Node.js timersPromises setInterval() to keep the event loop alive and do the gamepad polling.
#### `XInputGamepad(option: object): Class`
> This class extends EventEmitter from node:events
**Options**
- hz?: number (30)
This will determinate the polling rate. Usually 60hz (1000/60 = ~16ms) is used. If I'm not mistaken this is what the Chrome browser uses. But for our use case we don't need to poll that fast so it defaults to 30hz (~33ms). Increasing this value improves latency, but may cause a loss in performance due to more CPU time spent. The max accepted is 250hz (4ms).
- multitap?: boolean (true)
Scan for all 4 XInput slots to find any Gamepad. Set to false to only poll XInput slot 0 and potentially reduce the number of FFI calls per gamepad tick (event loop).
- joystickAsDPAD?: boolean (true)
Convert the left joystick analog axis to DPAD buttons. For our use case, driving a simple navigation menu, this is useful.
- inputFeedback?: boolean (false)
Vibrate shortly and lightly on any button activation. This is just for fun and/or debug.**Events**
`input(buttons: string[])`
List of activated buttons (human readable) of the first controller found.
A button is "activated" on press (button down) then release (button up).💡 NB: Triggers axis are converted into non standard XInput button name : `GAMEPAD_LEFT_TRIGGER` and `GAMEPAD_RIGHT_TRIGGER` (_on/off behavior_).
XInput Button names:
```
"XINPUT_GAMEPAD_DPAD_UP",
"XINPUT_GAMEPAD_DPAD_DOWN",
"XINPUT_GAMEPAD_DPAD_LEFT",
"XINPUT_GAMEPAD_DPAD_RIGHT",
"XINPUT_GAMEPAD_START",
"XINPUT_GAMEPAD_BACK",
"XINPUT_GAMEPAD_LEFT_THUMB",
"XINPUT_GAMEPAD_RIGHT_THUMB",
"XINPUT_GAMEPAD_LEFT_SHOULDER",
"XINPUT_GAMEPAD_RIGHT_SHOULDER",
"XINPUT_GAMEPAD_GUIDE",
"XINPUT_GAMEPAD_A",
"XINPUT_GAMEPAD_B",
"XINPUT_GAMEPAD_X",
"XINPUT_GAMEPAD_Y"
```💡 NB: XInput constants are available under the `constants` namespace.
```js
import { BUTTONS } from "xinput-ffi/constants";
//or
import { constants } from "xinput-ffi"
```Example:
```js
import { XInputGamepad } from "xinput-ffi";const gamepad = new XInputGamepad({ hz: 60 });
gamepad.on("input", (buttons)=>{
setImmediate(() => {
console.log(buttons);
});
});gamepad.poll();
```
**Methods**##### `poll()`
Start the gamepad event loop. This will keep the Node.js event loop going.
❌ Will throw on unexpected error.
##### `stop()`
Stop the gamepad event loop.
NB: This method will remove every event listener.
##### `pause()`
This function is meant to be called when an application loses focus.
_cf: [XInputEnable](https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputenable)_
##### `resume()`
This function is meant to be called when an application gains focus.
_cf: [XInputEnable](https://docs.microsoft.com/en-us/windows/win32/api/xinput/nf-xinput-xinputenable)_
#### `vibrate(option: object): Promise`
Vibrate the first controller found. Shorthand to the helper fn `rumble()`.
💡 Expose only `force` and `duration` options of `rumble()`.
❌ Will throw on error other than `ERROR_DEVICE_NOT_CONNECTED`.