deviceSourceManager.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import { DeviceInputSystem } from '../deviceInputSystem';
  2. import { Engine } from '../../Engines/engine';
  3. import { IDisposable } from '../../scene';
  4. import { DeviceType } from './deviceEnums';
  5. import { Nullable } from '../../types';
  6. import { Observable } from '../../Misc/observable';
  7. import { DeviceInput } from './deviceTypes';
  8. /**
  9. * Class that handles all input for a specific device
  10. */
  11. export class DeviceSource<T extends DeviceType> {
  12. // Public Members
  13. /**
  14. * Observable to handle device input changes per device
  15. */
  16. public readonly onInputChangedObservable = new Observable<{ inputIndex: DeviceInput<T>, previousState: Nullable<number>, currentState: Nullable<number> }>();
  17. // Private Members
  18. private readonly _deviceInputSystem: DeviceInputSystem;
  19. /**
  20. * Default Constructor
  21. * @param deviceInputSystem Reference to DeviceInputSystem
  22. * @param deviceType Type of device
  23. * @param deviceSlot "Slot" or index that device is referenced in
  24. */
  25. constructor(deviceInputSystem: DeviceInputSystem,
  26. /** Type of device */
  27. public readonly deviceType: DeviceType,
  28. /** "Slot" or index that device is referenced in */
  29. public readonly deviceSlot: number = 0) {
  30. this._deviceInputSystem = deviceInputSystem;
  31. }
  32. /**
  33. * Get input for specific input
  34. * @param inputIndex index of specific input on device
  35. * @returns Input value from DeviceInputSystem
  36. */
  37. public getInput(inputIndex: DeviceInput<T>): number {
  38. return this._deviceInputSystem.pollInput(this.deviceType, this.deviceSlot, inputIndex);
  39. }
  40. }
  41. /**
  42. * Class to keep track of devices
  43. */
  44. export class DeviceSourceManager implements IDisposable {
  45. // Public Members
  46. /**
  47. * Observable to be triggered when after a device is connected, any new observers added will be triggered against already connected devices
  48. */
  49. public readonly onDeviceConnectedObservable = new Observable<DeviceSource<DeviceType>>((observer) => {
  50. this.getDevices().forEach((device) => {
  51. this.onDeviceConnectedObservable.notifyObserver(observer, device);
  52. });
  53. });
  54. /**
  55. * Observable to be triggered when after a device is disconnected
  56. */
  57. public readonly onDeviceDisconnectedObservable = new Observable<DeviceSource<DeviceType>>();
  58. // Private Members
  59. private readonly _devices: Array<Array<DeviceSource<DeviceType>>>;
  60. private readonly _firstDevice: Array<number>;
  61. private readonly _deviceInputSystem: DeviceInputSystem;
  62. /**
  63. * Default Constructor
  64. * @param engine engine to pull input element from
  65. */
  66. constructor(engine: Engine) {
  67. const numberOfDeviceTypes = Object.keys(DeviceType).length / 2;
  68. this._devices = new Array<Array<DeviceSource<DeviceType>>>(numberOfDeviceTypes);
  69. this._firstDevice = new Array<number>(numberOfDeviceTypes);
  70. this._deviceInputSystem = DeviceInputSystem.Create(engine);
  71. this._deviceInputSystem.onDeviceConnected = (deviceType, deviceSlot) => {
  72. this._addDevice(deviceType, deviceSlot);
  73. this.onDeviceConnectedObservable.notifyObservers(this.getDeviceSource(deviceType, deviceSlot)!);
  74. };
  75. this._deviceInputSystem.onDeviceDisconnected = (deviceType, deviceSlot) => {
  76. const device = this.getDeviceSource(deviceType, deviceSlot)!; // Grab local reference to use before removing from devices
  77. this._removeDevice(deviceType, deviceSlot);
  78. this.onDeviceDisconnectedObservable.notifyObservers(device);
  79. };
  80. if (!this._deviceInputSystem.onInputChanged) {
  81. this._deviceInputSystem.onInputChanged = (deviceType, deviceSlot, inputIndex, previousState, currentState) => {
  82. this.getDeviceSource(deviceType, deviceSlot)?.onInputChangedObservable.notifyObservers({ inputIndex, previousState, currentState });
  83. };
  84. }
  85. }
  86. // Public Functions
  87. /**
  88. * Gets a DeviceSource, given a type and slot
  89. * @param deviceType Enum specifying device type
  90. * @param deviceSlot "Slot" or index that device is referenced in
  91. * @returns DeviceSource object
  92. */
  93. public getDeviceSource<T extends DeviceType>(deviceType: T, deviceSlot?: number): Nullable<DeviceSource<T>> {
  94. if (deviceSlot === undefined) {
  95. if (this._firstDevice[deviceType] === undefined) {
  96. return null;
  97. }
  98. deviceSlot = this._firstDevice[deviceType];
  99. }
  100. if (!this._devices[deviceType] || this._devices[deviceType][deviceSlot] === undefined) {
  101. return null;
  102. }
  103. return this._devices[deviceType][deviceSlot];
  104. }
  105. /**
  106. * Gets an array of DeviceSource objects for a given device type
  107. * @param deviceType Enum specifying device type
  108. * @returns Array of DeviceSource objects
  109. */
  110. public getDeviceSources<T extends DeviceType>(deviceType: T): ReadonlyArray<DeviceSource<T>> {
  111. return this._devices[deviceType].filter((source) => { return !!source; });
  112. }
  113. /**
  114. * Returns a read-only list of all available devices
  115. * @returns Read-only array with active devices
  116. */
  117. public getDevices(): ReadonlyArray<DeviceSource<DeviceType>> {
  118. const deviceArray = new Array<DeviceSource<DeviceType>>();
  119. this._devices.forEach((deviceSet) => {
  120. deviceArray.push.apply(deviceArray, deviceSet);
  121. });
  122. return deviceArray;
  123. }
  124. /**
  125. * Dispose of DeviceInputSystem and other parts
  126. */
  127. public dispose() {
  128. this.onDeviceConnectedObservable.clear();
  129. this.onDeviceDisconnectedObservable.clear();
  130. this._deviceInputSystem.dispose();
  131. }
  132. // Private Functions
  133. /**
  134. * Function to add device name to device list
  135. * @param deviceType Enum specifying device type
  136. * @param deviceSlot "Slot" or index that device is referenced in
  137. */
  138. private _addDevice(deviceType: DeviceType, deviceSlot: number) {
  139. if (!this._devices[deviceType]) {
  140. this._devices[deviceType] = new Array<DeviceSource<DeviceType>>();
  141. }
  142. if (!this._devices[deviceType][deviceSlot]) {
  143. this._devices[deviceType][deviceSlot] = new DeviceSource(this._deviceInputSystem, deviceType, deviceSlot);
  144. this._updateFirstDevices(deviceType);
  145. }
  146. }
  147. /**
  148. * Function to remove device name to device list
  149. * @param deviceType Enum specifying device type
  150. * @param deviceSlot "Slot" or index that device is referenced in
  151. */
  152. private _removeDevice(deviceType: DeviceType, deviceSlot: number) {
  153. delete this._devices[deviceType][deviceSlot];
  154. this._updateFirstDevices(deviceType);
  155. }
  156. /**
  157. * Updates array storing first connected device of each type
  158. * @param type Type of Device
  159. */
  160. private _updateFirstDevices(type: DeviceType) {
  161. switch (type) {
  162. case DeviceType.Keyboard:
  163. case DeviceType.Mouse:
  164. this._firstDevice[type] = 0;
  165. break;
  166. case DeviceType.Touch:
  167. case DeviceType.DualShock:
  168. case DeviceType.Xbox:
  169. case DeviceType.Switch:
  170. case DeviceType.Generic:
  171. const devices = this._devices[type];
  172. delete this._firstDevice[type];
  173. for (let i = 0; i < devices.length; i++) {
  174. if (devices[i]) {
  175. this._firstDevice[type] = i;
  176. break;
  177. }
  178. }
  179. break;
  180. }
  181. }
  182. }