InputManager.cs 153 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Unity.Collections;
  5. using UnityEngine.InputSystem.Composites;
  6. using UnityEngine.InputSystem.Controls;
  7. using Unity.Collections.LowLevel.Unsafe;
  8. using UnityEngine.Profiling;
  9. using UnityEngine.InputSystem.LowLevel;
  10. using UnityEngine.InputSystem.Processors;
  11. using UnityEngine.InputSystem.Interactions;
  12. using UnityEngine.InputSystem.Utilities;
  13. using UnityEngine.InputSystem.Layouts;
  14. #if UNITY_EDITOR
  15. using UnityEngine.InputSystem.Editor;
  16. #endif
  17. ////TODO: make diagnostics available in dev players and give it a public API to enable them
  18. ////TODO: work towards InputManager having no direct knowledge of actions
  19. ////TODO: allow pushing events into the system any which way; decouple from the buffer in NativeInputSystem being the only source
  20. ////TODO: make sure we discard events in editor updates when lockInputToGameView is true and the player isn't running or paused
  21. ////REVIEW: change the event properties over to using IObservable?
  22. ////REVIEW: instead of RegisterInteraction and RegisterProcessor, have a generic RegisterInterface (or something)?
  23. ////REVIEW: can we do away with the 'previous == previous frame' and simply buffer flip on every value write?
  24. ////REVIEW: should we force keeping mouse/pen/keyboard/touch around in editor even if not in list of supported devices?
  25. ////REVIEW: do we want to filter out state events that result in no state change?
  26. #pragma warning disable CS0649
  27. namespace UnityEngine.InputSystem
  28. {
  29. using DeviceChangeListener = Action<InputDevice, InputDeviceChange>;
  30. using DeviceStateChangeListener = Action<InputDevice, InputEventPtr>;
  31. using LayoutChangeListener = Action<string, InputControlLayoutChange>;
  32. using EventListener = Action<InputEventPtr, InputDevice>;
  33. using UpdateListener = Action;
  34. /// <summary>
  35. /// Hub of the input system.
  36. /// </summary>
  37. /// <remarks>
  38. /// Not exposed. Use <see cref="InputSystem"/> as the public entry point to the system.
  39. ///
  40. /// Manages devices, layouts, and event processing.
  41. /// </remarks>
  42. internal class InputManager
  43. {
  44. public ReadOnlyArray<InputDevice> devices => new ReadOnlyArray<InputDevice>(m_Devices, 0, m_DevicesCount);
  45. public TypeTable processors => m_Processors;
  46. public TypeTable interactions => m_Interactions;
  47. public TypeTable composites => m_Composites;
  48. public InputMetrics metrics
  49. {
  50. get
  51. {
  52. var result = m_Metrics;
  53. result.currentNumDevices = m_DevicesCount;
  54. result.currentStateSizeInBytes = (int)m_StateBuffers.totalSize;
  55. // Count controls.
  56. result.currentControlCount = m_DevicesCount;
  57. for (var i = 0; i < m_DevicesCount; ++i)
  58. result.currentControlCount += m_Devices[i].allControls.Count;
  59. // Count layouts.
  60. result.currentLayoutCount = m_Layouts.layoutTypes.Count;
  61. result.currentLayoutCount += m_Layouts.layoutStrings.Count;
  62. result.currentLayoutCount += m_Layouts.layoutBuilders.Count;
  63. result.currentLayoutCount += m_Layouts.layoutOverrides.Count;
  64. return result;
  65. }
  66. }
  67. public InputSettings settings
  68. {
  69. get
  70. {
  71. Debug.Assert(m_Settings != null);
  72. return m_Settings;
  73. }
  74. set
  75. {
  76. if (value == null)
  77. throw new ArgumentNullException(nameof(value));
  78. if (m_Settings == value)
  79. return;
  80. m_Settings = value;
  81. ApplySettings();
  82. }
  83. }
  84. public InputUpdateType updateMask
  85. {
  86. get => m_UpdateMask;
  87. set
  88. {
  89. // In editor, we don't allow disabling editor updates.
  90. #if UNITY_EDITOR
  91. value |= InputUpdateType.Editor;
  92. #endif
  93. if (m_UpdateMask == value)
  94. return;
  95. m_UpdateMask = value;
  96. // Recreate state buffers.
  97. if (m_DevicesCount > 0)
  98. ReallocateStateBuffers();
  99. }
  100. }
  101. public InputUpdateType defaultUpdateType
  102. {
  103. get
  104. {
  105. ////TODO: if we're *inside* an update, this should use the current update type
  106. #if UNITY_EDITOR
  107. if (!gameIsPlayingAndHasFocus)
  108. return InputUpdateType.Editor;
  109. #endif
  110. if ((m_UpdateMask & InputUpdateType.Manual) != 0)
  111. return InputUpdateType.Manual;
  112. if ((m_UpdateMask & InputUpdateType.Dynamic) != 0)
  113. return InputUpdateType.Dynamic;
  114. if ((m_UpdateMask & InputUpdateType.Fixed) != 0)
  115. return InputUpdateType.Fixed;
  116. return InputUpdateType.None;
  117. }
  118. }
  119. public float pollingFrequency
  120. {
  121. get => m_PollingFrequency;
  122. set
  123. {
  124. ////REVIEW: allow setting to zero to turn off polling altogether?
  125. if (value <= 0)
  126. throw new ArgumentException("Polling frequency must be greater than zero", "value");
  127. m_PollingFrequency = value;
  128. if (m_Runtime != null)
  129. m_Runtime.pollingFrequency = value;
  130. }
  131. }
  132. public event DeviceChangeListener onDeviceChange
  133. {
  134. add => m_DeviceChangeListeners.AppendWithCapacity(value);
  135. remove
  136. {
  137. var index = m_DeviceChangeListeners.IndexOf(value);
  138. if (index >= 0)
  139. m_DeviceChangeListeners.RemoveAtWithCapacity(index);
  140. }
  141. }
  142. public event DeviceStateChangeListener onDeviceStateChange
  143. {
  144. add => m_DeviceStateChangeListeners.AppendWithCapacity(value);
  145. remove
  146. {
  147. var index = m_DeviceStateChangeListeners.IndexOf(value);
  148. if (index >= 0)
  149. m_DeviceStateChangeListeners.RemoveAtWithCapacity(index);
  150. }
  151. }
  152. public event InputDeviceCommandDelegate onDeviceCommand
  153. {
  154. add => m_DeviceCommandCallbacks.Append(value);
  155. remove
  156. {
  157. var index = m_DeviceCommandCallbacks.IndexOf(value);
  158. if (index >= 0)
  159. m_DeviceCommandCallbacks.RemoveAtWithCapacity(index);
  160. }
  161. }
  162. ////REVIEW: would be great to have a way to sort out precedence between two callbacks
  163. public event InputDeviceFindControlLayoutDelegate onFindControlLayoutForDevice
  164. {
  165. add
  166. {
  167. m_DeviceFindLayoutCallbacks.AppendWithCapacity(value);
  168. // Having a new callback on this event can change the set of devices we recognize.
  169. // See if there's anything in the list of available devices that we can now turn
  170. // into an InputDevice whereas we couldn't before.
  171. //
  172. // NOTE: A callback could also impact already existing devices and theoretically alter
  173. // what layout we would have used for those. We do *NOT* retroactively apply
  174. // those changes.
  175. AddAvailableDevicesThatAreNowRecognized();
  176. }
  177. remove
  178. {
  179. var index = m_DeviceFindLayoutCallbacks.IndexOf(value);
  180. if (index >= 0)
  181. m_DeviceFindLayoutCallbacks.RemoveAtWithCapacity(index);
  182. }
  183. }
  184. public event LayoutChangeListener onLayoutChange
  185. {
  186. add => m_LayoutChangeListeners.AppendWithCapacity(value);
  187. remove
  188. {
  189. var index = m_LayoutChangeListeners.IndexOf(value);
  190. if (index >= 0)
  191. m_LayoutChangeListeners.RemoveAtWithCapacity(index);
  192. }
  193. }
  194. ////TODO: add InputEventBuffer struct that uses NativeArray underneath
  195. ////TODO: make InputEventTrace use NativeArray
  196. ////TODO: introduce an alternative that consumes events in bulk
  197. public event EventListener onEvent
  198. {
  199. add
  200. {
  201. if (!m_EventListeners.Contains(value))
  202. m_EventListeners.AppendWithCapacity(value);
  203. }
  204. remove
  205. {
  206. var index = m_EventListeners.IndexOf(value);
  207. if (index >= 0)
  208. m_EventListeners.RemoveAtWithCapacity(index);
  209. }
  210. }
  211. public event UpdateListener onBeforeUpdate
  212. {
  213. add
  214. {
  215. InstallBeforeUpdateHookIfNecessary();
  216. if (!m_BeforeUpdateListeners.Contains(value))
  217. m_BeforeUpdateListeners.AppendWithCapacity(value);
  218. }
  219. remove
  220. {
  221. var index = m_BeforeUpdateListeners.IndexOf(value);
  222. if (index >= 0)
  223. m_BeforeUpdateListeners.RemoveAtWithCapacity(index);
  224. }
  225. }
  226. public event UpdateListener onAfterUpdate
  227. {
  228. add
  229. {
  230. if (!m_AfterUpdateListeners.Contains(value))
  231. m_AfterUpdateListeners.AppendWithCapacity(value);
  232. }
  233. remove
  234. {
  235. var index = m_AfterUpdateListeners.IndexOf(value);
  236. if (index >= 0)
  237. m_AfterUpdateListeners.RemoveAtWithCapacity(index);
  238. }
  239. }
  240. public event Action onSettingsChange
  241. {
  242. add
  243. {
  244. if (!m_SettingsChangedListeners.Contains(value))
  245. m_SettingsChangedListeners.AppendWithCapacity(value);
  246. }
  247. remove
  248. {
  249. var index = m_SettingsChangedListeners.IndexOf(value);
  250. if (index >= 0)
  251. m_SettingsChangedListeners.RemoveAtWithCapacity(index);
  252. }
  253. }
  254. private bool gameIsPlayingAndHasFocus =>
  255. #if UNITY_EDITOR
  256. m_Runtime.isInPlayMode && !m_Runtime.isPaused && (m_HasFocus || InputEditorUserSettings.lockInputToGameView);
  257. #else
  258. true;
  259. #endif
  260. ////TODO: when registering a layout that exists as a layout of a different type (type vs string vs constructor),
  261. //// remove the existing registration
  262. // Add a layout constructed from a type.
  263. // If a layout with the same name already exists, the new layout
  264. // takes its place.
  265. public void RegisterControlLayout(string name, Type type)
  266. {
  267. if (string.IsNullOrEmpty(name))
  268. throw new ArgumentNullException(nameof(name));
  269. if (type == null)
  270. throw new ArgumentNullException(nameof(type));
  271. // Note that since InputDevice derives from InputControl, isDeviceLayout implies
  272. // isControlLayout to be true as well.
  273. var isDeviceLayout = typeof(InputDevice).IsAssignableFrom(type);
  274. var isControlLayout = typeof(InputControl).IsAssignableFrom(type);
  275. if (!isDeviceLayout && !isControlLayout)
  276. throw new ArgumentException($"Types used as layouts have to be InputControls or InputDevices; '{type.Name}' is a '{type.BaseType.Name}'",
  277. nameof(type));
  278. var internedName = new InternedString(name);
  279. var isReplacement = DoesLayoutExist(internedName);
  280. // All we do is enter the type into a map. We don't construct an InputControlLayout
  281. // from it until we actually need it in an InputDeviceBuilder to create a device.
  282. // This not only avoids us creating a bunch of objects on the managed heap but
  283. // also avoids us laboriously constructing a XRController layout, for example,
  284. // in a game that never uses XR.
  285. m_Layouts.layoutTypes[internedName] = type;
  286. ////TODO: make this independent of initialization order
  287. ////TODO: re-scan base type information after domain reloads
  288. // Walk class hierarchy all the way up to InputControl to see
  289. // if there's another type that's been registered as a layout.
  290. // If so, make it a base layout for this one.
  291. string baseLayout = null;
  292. for (var baseType = type.BaseType; baseLayout == null && baseType != typeof(InputControl);
  293. baseType = baseType.BaseType)
  294. {
  295. foreach (var entry in m_Layouts.layoutTypes)
  296. if (entry.Value == baseType)
  297. {
  298. baseLayout = entry.Key;
  299. break;
  300. }
  301. }
  302. PerformLayoutPostRegistration(internedName, new InlinedArray<InternedString>(new InternedString(baseLayout)),
  303. isReplacement, isKnownToBeDeviceLayout: isDeviceLayout);
  304. }
  305. public void RegisterControlLayout(string json, string name = null, bool isOverride = false)
  306. {
  307. if (string.IsNullOrEmpty(json))
  308. throw new ArgumentNullException(nameof(json));
  309. ////REVIEW: as long as no one has instantiated the layout, the base layout information is kinda pointless
  310. // Parse out name, device description, and base layout.
  311. InputControlLayout.ParseHeaderFieldsFromJson(json, out var nameFromJson, out var baseLayouts,
  312. out var deviceMatcher);
  313. // Decide whether to take name from JSON or from code.
  314. var internedLayoutName = new InternedString(name);
  315. if (internedLayoutName.IsEmpty())
  316. {
  317. internedLayoutName = nameFromJson;
  318. // Make sure we have a name.
  319. if (internedLayoutName.IsEmpty())
  320. throw new ArgumentException("Layout name has not been given and is not set in JSON layout",
  321. nameof(name));
  322. }
  323. // If it's an override, it must have a layout the overrides apply to.
  324. if (isOverride && baseLayouts.length == 0)
  325. {
  326. throw new ArgumentException(
  327. $"Layout override '{internedLayoutName}' must have 'extend' property mentioning layout to which to apply the overrides",
  328. nameof(json));
  329. }
  330. // Add it to our records.
  331. var isReplacement = DoesLayoutExist(internedLayoutName);
  332. m_Layouts.layoutStrings[internedLayoutName] = json;
  333. if (isOverride)
  334. {
  335. m_Layouts.layoutOverrideNames.Add(internedLayoutName);
  336. for (var i = 0; i < baseLayouts.length; ++i)
  337. {
  338. var baseLayoutName = baseLayouts[i];
  339. m_Layouts.layoutOverrides.TryGetValue(baseLayoutName, out var overrideList);
  340. ArrayHelpers.Append(ref overrideList, internedLayoutName);
  341. m_Layouts.layoutOverrides[baseLayoutName] = overrideList;
  342. }
  343. }
  344. PerformLayoutPostRegistration(internedLayoutName, baseLayouts,
  345. isReplacement: isReplacement, isOverride: isOverride);
  346. // If the layout contained a device matcher, register it.
  347. if (!deviceMatcher.empty)
  348. RegisterControlLayoutMatcher(internedLayoutName, deviceMatcher);
  349. }
  350. public void RegisterControlLayoutBuilder(Func<InputControlLayout> method, string name,
  351. string baseLayout = null)
  352. {
  353. if (method == null)
  354. throw new ArgumentNullException(nameof(method));
  355. if (string.IsNullOrEmpty(name))
  356. throw new ArgumentNullException(nameof(name));
  357. var internedLayoutName = new InternedString(name);
  358. var internedBaseLayoutName = new InternedString(baseLayout);
  359. var isReplacement = DoesLayoutExist(internedLayoutName);
  360. m_Layouts.layoutBuilders[internedLayoutName] = method;
  361. PerformLayoutPostRegistration(internedLayoutName, new InlinedArray<InternedString>(internedBaseLayoutName),
  362. isReplacement);
  363. }
  364. private void PerformLayoutPostRegistration(InternedString layoutName, InlinedArray<InternedString> baseLayouts,
  365. bool isReplacement, bool isKnownToBeDeviceLayout = false, bool isOverride = false)
  366. {
  367. ++m_LayoutRegistrationVersion;
  368. // Force-clear layout cache. Don't clear reference count so that
  369. // the cache gets cleared out properly when released in case someone
  370. // is using it ATM.
  371. InputControlLayout.s_CacheInstance.Clear();
  372. // For layouts that aren't overrides, add the name of the base
  373. // layout to the lookup table.
  374. if (!isOverride && baseLayouts.length > 0)
  375. {
  376. if (baseLayouts.length > 1)
  377. throw new NotSupportedException(
  378. $"Layout '{layoutName}' has multiple base layouts; this is only supported on layout overrides");
  379. var baseLayoutName = baseLayouts[0];
  380. if (!baseLayoutName.IsEmpty())
  381. m_Layouts.baseLayoutTable[layoutName] = baseLayoutName;
  382. }
  383. // Recreate any devices using the layout. If it's an override, recreate devices using any of the base layouts.
  384. if (isOverride)
  385. {
  386. for (var i = 0; i < baseLayouts.length; ++i)
  387. RecreateDevicesUsingLayout(baseLayouts[i], isKnownToBeDeviceLayout: isKnownToBeDeviceLayout);
  388. }
  389. else
  390. {
  391. RecreateDevicesUsingLayout(layoutName, isKnownToBeDeviceLayout: isKnownToBeDeviceLayout);
  392. }
  393. // In the editor, layouts may become available successively after a domain reload so
  394. // we may end up retaining device information all the way until we run the first full
  395. // player update. For every layout we register, we check here whether we have a saved
  396. // device state using a layout with the same name but not having a device description
  397. // (the latter is important as in that case, we should go through the normal matching
  398. // process and not just rely on the name of the layout). If so, we try here to recreate
  399. // the device with the just registered layout.
  400. #if UNITY_EDITOR
  401. for (var i = 0; i < m_SavedDeviceStates.LengthSafe(); ++i)
  402. {
  403. ref var deviceState = ref m_SavedDeviceStates[i];
  404. if (layoutName != deviceState.layout || !deviceState.description.empty)
  405. continue;
  406. if (RestoreDeviceFromSavedState(ref deviceState, layoutName))
  407. {
  408. ArrayHelpers.EraseAt(ref m_SavedDeviceStates, i);
  409. --i;
  410. }
  411. }
  412. #endif
  413. // Let listeners know.
  414. var change = isReplacement ? InputControlLayoutChange.Replaced : InputControlLayoutChange.Added;
  415. for (var i = 0; i < m_LayoutChangeListeners.length; ++i)
  416. m_LayoutChangeListeners[i](layoutName.ToString(), change);
  417. }
  418. private void RecreateDevicesUsingLayout(InternedString layout, bool isKnownToBeDeviceLayout = false)
  419. {
  420. if (m_DevicesCount == 0)
  421. return;
  422. List<InputDevice> devicesUsingLayout = null;
  423. // Find all devices using the layout.
  424. for (var i = 0; i < m_DevicesCount; ++i)
  425. {
  426. var device = m_Devices[i];
  427. bool usesLayout;
  428. if (isKnownToBeDeviceLayout)
  429. usesLayout = IsControlUsingLayout(device, layout);
  430. else
  431. usesLayout = IsControlOrChildUsingLayoutRecursive(device, layout);
  432. if (usesLayout)
  433. {
  434. if (devicesUsingLayout == null)
  435. devicesUsingLayout = new List<InputDevice>();
  436. devicesUsingLayout.Add(device);
  437. }
  438. }
  439. // If there's none, we're good.
  440. if (devicesUsingLayout == null)
  441. return;
  442. // Remove and re-add the matching devices.
  443. using (InputDeviceBuilder.Ref())
  444. {
  445. for (var i = 0; i < devicesUsingLayout.Count; ++i)
  446. {
  447. var device = devicesUsingLayout[i];
  448. RecreateDevice(device, device.m_Layout);
  449. }
  450. }
  451. }
  452. private bool IsControlOrChildUsingLayoutRecursive(InputControl control, InternedString layout)
  453. {
  454. // Check control itself.
  455. if (IsControlUsingLayout(control, layout))
  456. return true;
  457. // Check children.
  458. var children = control.children;
  459. for (var i = 0; i < children.Count; ++i)
  460. if (IsControlOrChildUsingLayoutRecursive(children[i], layout))
  461. return true;
  462. return false;
  463. }
  464. private bool IsControlUsingLayout(InputControl control, InternedString layout)
  465. {
  466. // Check direct match.
  467. if (control.layout == layout)
  468. return true;
  469. // Check base layout chain.
  470. var baseLayout = control.m_Layout;
  471. while (m_Layouts.baseLayoutTable.TryGetValue(baseLayout, out baseLayout))
  472. if (baseLayout == layout)
  473. return true;
  474. return false;
  475. }
  476. public void RegisterControlLayoutMatcher(string layoutName, InputDeviceMatcher matcher)
  477. {
  478. if (string.IsNullOrEmpty(layoutName))
  479. throw new ArgumentNullException(nameof(layoutName));
  480. if (matcher.empty)
  481. throw new ArgumentException("Matcher cannot be empty", nameof(matcher));
  482. // Add to table.
  483. var internedLayoutName = new InternedString(layoutName);
  484. m_Layouts.AddMatcher(internedLayoutName, matcher);
  485. // Recreate any device that we match better than its current layout.
  486. RecreateDevicesUsingLayoutWithInferiorMatch(matcher);
  487. // See if we can make sense of any device we couldn't make sense of before.
  488. AddAvailableDevicesMatchingDescription(matcher, internedLayoutName);
  489. }
  490. public void RegisterControlLayoutMatcher(Type type, InputDeviceMatcher matcher)
  491. {
  492. if (type == null)
  493. throw new ArgumentNullException(nameof(type));
  494. if (matcher.empty)
  495. throw new ArgumentException("Matcher cannot be empty", nameof(matcher));
  496. var layoutName = m_Layouts.TryFindLayoutForType(type);
  497. if (layoutName.IsEmpty())
  498. throw new ArgumentException(
  499. $"Type '{type.Name}' has not been registered as a control layout", nameof(type));
  500. RegisterControlLayoutMatcher(layoutName, matcher);
  501. }
  502. private void RecreateDevicesUsingLayoutWithInferiorMatch(InputDeviceMatcher deviceMatcher)
  503. {
  504. if (m_DevicesCount == 0)
  505. return;
  506. using (InputDeviceBuilder.Ref())
  507. {
  508. var deviceCount = m_DevicesCount;
  509. for (var i = 0; i < deviceCount; ++i)
  510. {
  511. var device = m_Devices[i];
  512. var deviceDescription = device.description;
  513. if (deviceDescription.empty || !(deviceMatcher.MatchPercentage(deviceDescription) > 0))
  514. continue;
  515. var layoutName = TryFindMatchingControlLayout(ref deviceDescription, device.deviceId);
  516. if (layoutName != device.m_Layout)
  517. {
  518. device.m_Description = deviceDescription;
  519. RecreateDevice(device, layoutName);
  520. // We're removing devices in the middle of the array and appending
  521. // them at the end. Adjust our index and device count to make sure
  522. // we're not iterating all the way into already processed devices.
  523. --i;
  524. --deviceCount;
  525. }
  526. }
  527. }
  528. }
  529. private void RecreateDevice(InputDevice oldDevice, InternedString newLayout)
  530. {
  531. // Remove.
  532. RemoveDevice(oldDevice, keepOnListOfAvailableDevices: true);
  533. // Re-setup device.
  534. var newDevice = InputDevice.Build<InputDevice>(newLayout, oldDevice.m_Variants,
  535. deviceDescription: oldDevice.m_Description);
  536. // Preserve device properties that should not be changed by the re-creation
  537. // of a device.
  538. newDevice.m_DeviceId = oldDevice.m_DeviceId;
  539. newDevice.m_Description = oldDevice.m_Description;
  540. if (oldDevice.native)
  541. newDevice.m_DeviceFlags |= InputDevice.DeviceFlags.Native;
  542. if (oldDevice.remote)
  543. newDevice.m_DeviceFlags |= InputDevice.DeviceFlags.Remote;
  544. if (!oldDevice.enabled)
  545. {
  546. newDevice.m_DeviceFlags |= InputDevice.DeviceFlags.DisabledStateHasBeenQueried;
  547. newDevice.m_DeviceFlags |= InputDevice.DeviceFlags.Disabled;
  548. }
  549. // Re-add.
  550. AddDevice(newDevice);
  551. }
  552. private void AddAvailableDevicesMatchingDescription(InputDeviceMatcher matcher, InternedString layout)
  553. {
  554. #if UNITY_EDITOR
  555. // If we still have some devices saved from the last domain reload, see
  556. // if they are matched by the given matcher. If so, turn them into devices.
  557. for (var i = 0; i < m_SavedDeviceStates.LengthSafe(); ++i)
  558. {
  559. ref var deviceState = ref m_SavedDeviceStates[i];
  560. if (matcher.MatchPercentage(deviceState.description) > 0)
  561. {
  562. RestoreDeviceFromSavedState(ref deviceState, layout);
  563. ArrayHelpers.EraseAt(ref m_SavedDeviceStates, i);
  564. --i;
  565. }
  566. }
  567. #endif
  568. // See if the new description to layout mapping allows us to make
  569. // sense of a device we couldn't make sense of so far.
  570. for (var i = 0; i < m_AvailableDeviceCount; ++i)
  571. {
  572. // Ignore if it's a device that has been explicitly removed.
  573. if (m_AvailableDevices[i].isRemoved)
  574. continue;
  575. var deviceId = m_AvailableDevices[i].deviceId;
  576. if (TryGetDeviceById(deviceId) != null)
  577. continue;
  578. if (matcher.MatchPercentage(m_AvailableDevices[i].description) > 0f)
  579. {
  580. // Try to create InputDevice instance.
  581. try
  582. {
  583. AddDevice(layout, deviceId, deviceDescription: m_AvailableDevices[i].description,
  584. deviceFlags: m_AvailableDevices[i].isNative ? InputDevice.DeviceFlags.Native : 0);
  585. }
  586. catch (Exception exception)
  587. {
  588. Debug.LogError(
  589. $"Layout '{layout}' matches existing device '{m_AvailableDevices[i].description}' but failed to instantiate: {exception}");
  590. Debug.LogException(exception);
  591. continue;
  592. }
  593. // Re-enable device.
  594. var command = EnableDeviceCommand.Create();
  595. m_Runtime.DeviceCommand(deviceId, ref command);
  596. }
  597. }
  598. }
  599. public void RemoveControlLayout(string name, string @namespace = null)
  600. {
  601. if (string.IsNullOrEmpty(name))
  602. throw new ArgumentNullException(nameof(name));
  603. if (@namespace != null)
  604. name = $"{@namespace}::{name}";
  605. var internedName = new InternedString(name);
  606. // Remove all devices using the layout.
  607. for (var i = 0; i < m_DevicesCount;)
  608. {
  609. var device = m_Devices[i];
  610. if (IsControlOrChildUsingLayoutRecursive(device, internedName))
  611. {
  612. RemoveDevice(device, keepOnListOfAvailableDevices: true);
  613. }
  614. else
  615. {
  616. ++i;
  617. }
  618. }
  619. // Remove layout record.
  620. m_Layouts.layoutTypes.Remove(internedName);
  621. m_Layouts.layoutStrings.Remove(internedName);
  622. m_Layouts.layoutBuilders.Remove(internedName);
  623. m_Layouts.baseLayoutTable.Remove(internedName);
  624. ////TODO: check all layout inheritance chain for whether they are based on the layout and if so
  625. //// remove those layouts, too
  626. // Let listeners know.
  627. for (var i = 0; i < m_LayoutChangeListeners.length; ++i)
  628. m_LayoutChangeListeners[i](name, InputControlLayoutChange.Removed);
  629. }
  630. public InputControlLayout TryLoadControlLayout(Type type)
  631. {
  632. if (type == null)
  633. throw new ArgumentNullException(nameof(type));
  634. if (!typeof(InputControl).IsAssignableFrom(type))
  635. throw new ArgumentException($"Type '{type.Name}' is not an InputControl", nameof(type));
  636. // Find the layout name that the given type was registered with.
  637. var layoutName = m_Layouts.TryFindLayoutForType(type);
  638. if (layoutName.IsEmpty())
  639. throw new ArgumentException(
  640. $"Type '{type.Name}' has not been registered as a control layout", nameof(type));
  641. return m_Layouts.TryLoadLayout(layoutName);
  642. }
  643. public InputControlLayout TryLoadControlLayout(InternedString name)
  644. {
  645. return m_Layouts.TryLoadLayout(name);
  646. }
  647. ////FIXME: allowing the description to be modified as part of this is surprising; find a better way
  648. public InternedString TryFindMatchingControlLayout(ref InputDeviceDescription deviceDescription, int deviceId = InputDevice.InvalidDeviceId)
  649. {
  650. Profiler.BeginSample("InputSystem.TryFindMatchingControlLayout");
  651. ////TODO: this will want to take overrides into account
  652. // See if we can match by description.
  653. var layoutName = m_Layouts.TryFindMatchingLayout(deviceDescription);
  654. if (layoutName.IsEmpty())
  655. {
  656. // No, so try to match by device class. If we have a "Gamepad" layout,
  657. // for example, a device that classifies itself as a "Gamepad" will match
  658. // that layout.
  659. //
  660. // NOTE: Have to make sure here that we get a device layout and not a
  661. // control layout.
  662. if (!string.IsNullOrEmpty(deviceDescription.deviceClass))
  663. {
  664. var deviceClassLowerCase = new InternedString(deviceDescription.deviceClass);
  665. var type = m_Layouts.GetControlTypeForLayout(deviceClassLowerCase);
  666. if (type != null && typeof(InputDevice).IsAssignableFrom(type))
  667. layoutName = new InternedString(deviceDescription.deviceClass);
  668. }
  669. }
  670. ////REVIEW: listeners registering new layouts from in here may potentially lead to the creation of devices; should we disallow that?
  671. ////REVIEW: if a callback picks a layout, should we re-run through the list of callbacks? or should we just remove haveOverridenLayoutName?
  672. // Give listeners a shot to select/create a layout.
  673. if (m_DeviceFindLayoutCallbacks.length > 0)
  674. {
  675. // First time we get here, put our delegate for executing device commands
  676. // in place. We wrap the call to IInputRuntime.DeviceCommand so that we don't
  677. // need to expose the runtime to the onFindLayoutForDevice callbacks.
  678. if (m_DeviceFindExecuteCommandDelegate == null)
  679. m_DeviceFindExecuteCommandDelegate =
  680. (ref InputDeviceCommand commandRef) =>
  681. {
  682. if (m_DeviceFindExecuteCommandDeviceId == InputDevice.InvalidDeviceId)
  683. return InputDeviceCommand.GenericFailure;
  684. return m_Runtime.DeviceCommand(m_DeviceFindExecuteCommandDeviceId, ref commandRef);
  685. };
  686. m_DeviceFindExecuteCommandDeviceId = deviceId;
  687. var haveOverriddenLayoutName = false;
  688. for (var i = 0; i < m_DeviceFindLayoutCallbacks.length; ++i)
  689. {
  690. var newLayout = m_DeviceFindLayoutCallbacks[i](ref deviceDescription, layoutName,
  691. m_DeviceFindExecuteCommandDelegate);
  692. if (!string.IsNullOrEmpty(newLayout) && !haveOverriddenLayoutName)
  693. {
  694. layoutName = new InternedString(newLayout);
  695. haveOverriddenLayoutName = true;
  696. }
  697. }
  698. }
  699. Profiler.EndSample();
  700. return layoutName;
  701. }
  702. /// <summary>
  703. /// Return true if the given device layout is supported by the game according to <see cref="InputSettings.supportedDevices"/>.
  704. /// </summary>
  705. /// <param name="layoutName">Name of the device layout.</param>
  706. /// <returns>True if a device with the given layout should be created for the game, false otherwise.</returns>
  707. private bool IsDeviceLayoutMarkedAsSupportedInSettings(InternedString layoutName)
  708. {
  709. // In the editor, "Supported Devices" can be overridden by a user setting. This causes
  710. // all available devices to be added regardless of what "Supported Devices" says. This
  711. // is useful to ensure that things like keyboard, mouse, and pen keep working in the editor
  712. // even if not supported as devices in the game.
  713. #if UNITY_EDITOR
  714. if (InputEditorUserSettings.addDevicesNotSupportedByProject)
  715. return true;
  716. #endif
  717. var supportedDevices = m_Settings.supportedDevices;
  718. if (supportedDevices.Count == 0)
  719. {
  720. // If supportedDevices is empty, all device layouts are considered supported.
  721. return true;
  722. }
  723. for (var n = 0; n < supportedDevices.Count; ++n)
  724. {
  725. var supportedLayout = new InternedString(supportedDevices[n]);
  726. if (layoutName == supportedLayout || m_Layouts.IsBasedOn(supportedLayout, layoutName))
  727. return true;
  728. }
  729. return false;
  730. }
  731. private bool DoesLayoutExist(InternedString name)
  732. {
  733. return m_Layouts.layoutTypes.ContainsKey(name) ||
  734. m_Layouts.layoutStrings.ContainsKey(name) ||
  735. m_Layouts.layoutBuilders.ContainsKey(name);
  736. }
  737. public IEnumerable<string> ListControlLayouts(string basedOn = null)
  738. {
  739. ////FIXME: this may add a name twice
  740. if (!string.IsNullOrEmpty(basedOn))
  741. {
  742. var internedBasedOn = new InternedString(basedOn);
  743. foreach (var entry in m_Layouts.layoutTypes)
  744. if (m_Layouts.IsBasedOn(internedBasedOn, entry.Key))
  745. yield return entry.Key;
  746. foreach (var entry in m_Layouts.layoutStrings)
  747. if (m_Layouts.IsBasedOn(internedBasedOn, entry.Key))
  748. yield return entry.Key;
  749. foreach (var entry in m_Layouts.layoutBuilders)
  750. if (m_Layouts.IsBasedOn(internedBasedOn, entry.Key))
  751. yield return entry.Key;
  752. }
  753. else
  754. {
  755. foreach (var entry in m_Layouts.layoutTypes)
  756. yield return entry.Key;
  757. foreach (var entry in m_Layouts.layoutStrings)
  758. yield return entry.Key;
  759. foreach (var entry in m_Layouts.layoutBuilders)
  760. yield return entry.Key;
  761. }
  762. }
  763. // Adds all controls that match the given path spec to the given list.
  764. // Returns number of controls added to the list.
  765. // NOTE: Does not create garbage.
  766. /// <summary>
  767. /// Adds to the given list all controls that match the given <see cref="InputControlPath">path spec</see>
  768. /// and are assignable to the given type.
  769. /// </summary>
  770. /// <param name="path"></param>
  771. /// <param name="controls"></param>
  772. /// <typeparam name="TControl"></typeparam>
  773. /// <returns></returns>
  774. public int GetControls<TControl>(string path, ref InputControlList<TControl> controls)
  775. where TControl : InputControl
  776. {
  777. if (string.IsNullOrEmpty(path))
  778. return 0;
  779. if (m_DevicesCount == 0)
  780. return 0;
  781. var deviceCount = m_DevicesCount;
  782. var numMatches = 0;
  783. for (var i = 0; i < deviceCount; ++i)
  784. {
  785. var device = m_Devices[i];
  786. numMatches += InputControlPath.TryFindControls(device, path, 0, ref controls);
  787. }
  788. return numMatches;
  789. }
  790. public void SetDeviceUsage(InputDevice device, InternedString usage)
  791. {
  792. if (device == null)
  793. throw new ArgumentNullException(nameof(device));
  794. if (device.usages.Count == 1 && device.usages[0] == usage)
  795. return;
  796. if (device.usages.Count == 0 && usage.IsEmpty())
  797. return;
  798. device.ClearDeviceUsages();
  799. if (!usage.IsEmpty())
  800. device.AddDeviceUsage(usage);
  801. NotifyUsageChanged(device);
  802. }
  803. public void AddDeviceUsage(InputDevice device, InternedString usage)
  804. {
  805. if (device == null)
  806. throw new ArgumentNullException(nameof(device));
  807. if (usage.IsEmpty())
  808. throw new ArgumentException("Usage string cannot be empty", nameof(usage));
  809. if (device.usages.Contains(usage))
  810. return;
  811. device.AddDeviceUsage(usage);
  812. NotifyUsageChanged(device);
  813. }
  814. public void RemoveDeviceUsage(InputDevice device, InternedString usage)
  815. {
  816. if (device == null)
  817. throw new ArgumentNullException(nameof(device));
  818. if (usage.IsEmpty())
  819. throw new ArgumentException("Usage string cannot be empty", nameof(usage));
  820. if (!device.usages.Contains(usage))
  821. return;
  822. device.RemoveDeviceUsage(usage);
  823. NotifyUsageChanged(device);
  824. }
  825. private void NotifyUsageChanged(InputDevice device)
  826. {
  827. InputActionState.OnDeviceChange(device, InputDeviceChange.UsageChanged);
  828. // Notify listeners.
  829. for (var i = 0; i < m_DeviceChangeListeners.length; ++i)
  830. m_DeviceChangeListeners[i](device, InputDeviceChange.UsageChanged);
  831. ////REVIEW: This was for the XRController leftHand and rightHand getters but these do lookups dynamically now; remove?
  832. // Usage may affect current device so update.
  833. device.MakeCurrent();
  834. }
  835. ////TODO: make sure that no device or control with a '/' in the name can creep into the system
  836. public InputDevice AddDevice(Type type, string name = null)
  837. {
  838. if (type == null)
  839. throw new ArgumentNullException(nameof(type));
  840. // Find the layout name that the given type was registered with.
  841. var layoutName = m_Layouts.TryFindLayoutForType(type);
  842. if (layoutName.IsEmpty())
  843. {
  844. // Automatically register the given type as a layout.
  845. if (layoutName.IsEmpty())
  846. {
  847. layoutName = new InternedString(type.Name);
  848. RegisterControlLayout(type.Name, type);
  849. }
  850. }
  851. Debug.Assert(!layoutName.IsEmpty(), name);
  852. // Note that since we go through the normal by-name lookup here, this will
  853. // still work if the layout from the type was override with a string layout.
  854. return AddDevice(layoutName, name);
  855. }
  856. // Creates a device from the given layout and adds it to the system.
  857. // NOTE: Creates garbage.
  858. public InputDevice AddDevice(string layout, string name = null, InternedString variants = new InternedString())
  859. {
  860. if (string.IsNullOrEmpty(layout))
  861. throw new ArgumentNullException(nameof(layout));
  862. var device = InputDevice.Build<InputDevice>(layout, variants);
  863. if (!string.IsNullOrEmpty(name))
  864. device.m_Name = new InternedString(name);
  865. AddDevice(device);
  866. return device;
  867. }
  868. // Add device with a forced ID. Used when creating devices reported to us by native.
  869. private InputDevice AddDevice(InternedString layout, int deviceId,
  870. string deviceName = null,
  871. InputDeviceDescription deviceDescription = new InputDeviceDescription(),
  872. InputDevice.DeviceFlags deviceFlags = 0,
  873. InternedString variants = default)
  874. {
  875. var device = InputDevice.Build<InputDevice>(new InternedString(layout),
  876. deviceDescription: deviceDescription,
  877. layoutVariants: variants);
  878. device.m_DeviceId = deviceId;
  879. device.m_Description = deviceDescription;
  880. device.m_DeviceFlags |= deviceFlags;
  881. if (!string.IsNullOrEmpty(deviceName))
  882. device.m_Name = new InternedString(deviceName);
  883. // Default display name to product name.
  884. if (!string.IsNullOrEmpty(deviceDescription.product))
  885. device.m_DisplayName = deviceDescription.product;
  886. AddDevice(device);
  887. return device;
  888. }
  889. public void AddDevice(InputDevice device)
  890. {
  891. if (device == null)
  892. throw new ArgumentNullException(nameof(device));
  893. if (string.IsNullOrEmpty(device.layout))
  894. throw new InvalidOperationException("Device has no associated layout");
  895. // Ignore if the same device gets added multiple times.
  896. if (ArrayHelpers.Contains(m_Devices, device))
  897. return;
  898. MakeDeviceNameUnique(device);
  899. AssignUniqueDeviceId(device);
  900. // Add to list.
  901. device.m_DeviceIndex = ArrayHelpers.AppendWithCapacity(ref m_Devices, ref m_DevicesCount, device);
  902. ////REVIEW: Not sure a full-blown dictionary is the right way here. Alternatives are to keep
  903. //// a sparse array that directly indexes using the linearly increasing IDs (though that
  904. //// may get large over time). Or to just do a linear search through m_Devices (but
  905. //// that may end up tapping a bunch of memory locations in the heap to find the right
  906. //// device; could be improved by sorting m_Devices by ID and picking a good starting
  907. //// point based on the ID we have instead of searching from [0] always).
  908. m_DevicesById[device.deviceId] = device;
  909. // Let InputStateBuffers know this device doesn't have any associated state yet.
  910. device.m_StateBlock.byteOffset = InputStateBlock.InvalidOffset;
  911. // Update state buffers.
  912. ReallocateStateBuffers();
  913. InitializeDefaultState(device);
  914. InitializeNoiseMask(device);
  915. // Update metrics.
  916. m_Metrics.maxNumDevices = Mathf.Max(m_DevicesCount, m_Metrics.maxNumDevices);
  917. m_Metrics.maxStateSizeInBytes = Mathf.Max((int)m_StateBuffers.totalSize, m_Metrics.maxStateSizeInBytes);
  918. // Make sure that if the device ID is listed in m_AvailableDevices, the device
  919. // is no longer marked as removed.
  920. for (var i = 0; i < m_AvailableDeviceCount; ++i)
  921. {
  922. if (m_AvailableDevices[i].deviceId == device.deviceId)
  923. m_AvailableDevices[i].isRemoved = false;
  924. }
  925. ////REVIEW: we may want to suppress this during the initial device discovery phase
  926. // Let actions re-resolve their paths.
  927. InputActionState.OnDeviceChange(device, InputDeviceChange.Added);
  928. // If the device wants automatic callbacks before input updates,
  929. // put it on the list.
  930. if (device is IInputUpdateCallbackReceiver beforeUpdateCallbackReceiver)
  931. onBeforeUpdate += beforeUpdateCallbackReceiver.OnUpdate;
  932. // If the device has state callbacks, make a note of it.
  933. if (device is IInputStateCallbackReceiver)
  934. {
  935. InstallBeforeUpdateHookIfNecessary();
  936. device.m_DeviceFlags |= InputDevice.DeviceFlags.HasStateCallbacks;
  937. m_HaveDevicesWithStateCallbackReceivers = true;
  938. }
  939. // If the device wants before-render updates, enable them if they
  940. // aren't already.
  941. if (device.updateBeforeRender)
  942. updateMask |= InputUpdateType.BeforeRender;
  943. // Notify device.
  944. device.NotifyAdded();
  945. ////REVIEW: is this really a good thing to do? just plugging in a device shouldn't make
  946. //// it current, no?
  947. // Make the device current.
  948. device.MakeCurrent();
  949. // Notify listeners.
  950. for (var i = 0; i < m_DeviceChangeListeners.length; ++i)
  951. m_DeviceChangeListeners[i](device, InputDeviceChange.Added);
  952. }
  953. ////TODO: this path should really put the device on the list of available devices
  954. ////TODO: this path should discover disconnected devices
  955. public InputDevice AddDevice(InputDeviceDescription description)
  956. {
  957. ////REVIEW: is throwing here really such a useful thing?
  958. return AddDevice(description, throwIfNoLayoutFound: true);
  959. }
  960. public InputDevice AddDevice(InputDeviceDescription description, bool throwIfNoLayoutFound,
  961. string deviceName = null, int deviceId = InputDevice.InvalidDeviceId, InputDevice.DeviceFlags deviceFlags = 0)
  962. {
  963. Profiler.BeginSample("InputSystem.AddDevice");
  964. // Look for matching layout.
  965. var layout = TryFindMatchingControlLayout(ref description, deviceId);
  966. // If no layout was found, bail out.
  967. if (layout.IsEmpty())
  968. {
  969. if (throwIfNoLayoutFound)
  970. throw new ArgumentException($"Cannot find layout matching device description '{description}'", nameof(description));
  971. // If it's a device coming from the runtime, disable it.
  972. if (deviceId != InputDevice.InvalidDeviceId)
  973. {
  974. var command = DisableDeviceCommand.Create();
  975. m_Runtime.DeviceCommand(deviceId, ref command);
  976. }
  977. Profiler.EndSample();
  978. return null;
  979. }
  980. var device = AddDevice(layout, deviceId, deviceName, description, deviceFlags);
  981. device.m_Description = description;
  982. Profiler.EndSample();
  983. return device;
  984. }
  985. public void RemoveDevice(InputDevice device, bool keepOnListOfAvailableDevices = false)
  986. {
  987. if (device == null)
  988. throw new ArgumentNullException(nameof(device));
  989. // If device has not been added, ignore.
  990. if (device.m_DeviceIndex == InputDevice.kInvalidDeviceIndex)
  991. return;
  992. // Remove state monitors while device index is still valid.
  993. RemoveStateChangeMonitors(device);
  994. // Remove from device array.
  995. var deviceIndex = device.m_DeviceIndex;
  996. var deviceId = device.deviceId;
  997. if (deviceIndex < m_StateChangeMonitors.LengthSafe())
  998. {
  999. // m_StateChangeMonitors mirrors layout of m_Devices *but* may be shorter.
  1000. var count = m_StateChangeMonitors.Length;
  1001. ArrayHelpers.EraseAtWithCapacity(m_StateChangeMonitors, ref count, deviceIndex);
  1002. }
  1003. ArrayHelpers.EraseAtWithCapacity(m_Devices, ref m_DevicesCount, deviceIndex);
  1004. m_DevicesById.Remove(deviceId);
  1005. if (m_Devices != null)
  1006. {
  1007. // Remove from state buffers.
  1008. ReallocateStateBuffers();
  1009. }
  1010. else
  1011. {
  1012. // No more devices. Kill state buffers.
  1013. m_StateBuffers.FreeAll();
  1014. }
  1015. // Update device indices. Do this after reallocating state buffers as that call requires
  1016. // the old indices to still be in place.
  1017. for (var i = deviceIndex; i < m_DevicesCount; ++i)
  1018. --m_Devices[i].m_DeviceIndex; // Indices have shifted down by one.
  1019. device.m_DeviceIndex = InputDevice.kInvalidDeviceIndex;
  1020. // Update list of available devices.
  1021. for (var i = 0; i < m_AvailableDeviceCount; ++i)
  1022. {
  1023. if (m_AvailableDevices[i].deviceId == deviceId)
  1024. {
  1025. if (keepOnListOfAvailableDevices)
  1026. m_AvailableDevices[i].isRemoved = true;
  1027. else
  1028. ArrayHelpers.EraseAtWithCapacity(m_AvailableDevices, ref m_AvailableDeviceCount, i);
  1029. break;
  1030. }
  1031. }
  1032. // Unbake offset into global state buffers.
  1033. device.BakeOffsetIntoStateBlockRecursive((uint)-device.m_StateBlock.byteOffset);
  1034. // Force enabled actions to remove controls from the device.
  1035. // We've already set the device index to be invalid so we any attempts
  1036. // by actions to uninstall state monitors will get ignored.
  1037. InputActionState.OnDeviceChange(device, InputDeviceChange.Removed);
  1038. // Kill before update callback, if applicable.
  1039. if (device is IInputUpdateCallbackReceiver beforeUpdateCallbackReceiver)
  1040. onBeforeUpdate -= beforeUpdateCallbackReceiver.OnUpdate;
  1041. // Disable before-render updates if this was the last device
  1042. // that requires them.
  1043. if (device.updateBeforeRender)
  1044. {
  1045. var haveDeviceRequiringBeforeRender = false;
  1046. for (var i = 0; i < m_DevicesCount; ++i)
  1047. if (m_Devices[i].updateBeforeRender)
  1048. {
  1049. haveDeviceRequiringBeforeRender = true;
  1050. break;
  1051. }
  1052. if (!haveDeviceRequiringBeforeRender)
  1053. updateMask &= ~InputUpdateType.BeforeRender;
  1054. }
  1055. // Let device know.
  1056. device.NotifyRemoved();
  1057. // Let listeners know.
  1058. for (var i = 0; i < m_DeviceChangeListeners.length; ++i)
  1059. m_DeviceChangeListeners[i](device, InputDeviceChange.Removed);
  1060. }
  1061. public void FlushDisconnectedDevices()
  1062. {
  1063. m_DisconnectedDevices.Clear(m_DisconnectedDevicesCount);
  1064. m_DisconnectedDevicesCount = 0;
  1065. }
  1066. public InputDevice TryGetDevice(string nameOrLayout)
  1067. {
  1068. if (string.IsNullOrEmpty(nameOrLayout))
  1069. throw new ArgumentException("Name is null or empty.", nameof(nameOrLayout));
  1070. if (m_DevicesCount == 0)
  1071. return null;
  1072. var nameOrLayoutLowerCase = nameOrLayout.ToLower();
  1073. for (var i = 0; i < m_DevicesCount; ++i)
  1074. {
  1075. var device = m_Devices[i];
  1076. if (device.m_Name.ToLower() == nameOrLayoutLowerCase ||
  1077. device.m_Layout.ToLower() == nameOrLayoutLowerCase)
  1078. return device;
  1079. }
  1080. return null;
  1081. }
  1082. public InputDevice GetDevice(string nameOrLayout)
  1083. {
  1084. var device = TryGetDevice(nameOrLayout);
  1085. if (device == null)
  1086. throw new ArgumentException($"Cannot find device with name or layout '{nameOrLayout}'", nameof(nameOrLayout));
  1087. return device;
  1088. }
  1089. public InputDevice TryGetDevice(Type layoutType)
  1090. {
  1091. var layoutName = m_Layouts.TryFindLayoutForType(layoutType);
  1092. if (layoutName.IsEmpty())
  1093. return null;
  1094. return TryGetDevice(layoutName);
  1095. }
  1096. public InputDevice TryGetDeviceById(int id)
  1097. {
  1098. if (m_DevicesById.TryGetValue(id, out var result))
  1099. return result;
  1100. return null;
  1101. }
  1102. // Adds any device that's been reported to the system but could not be matched to
  1103. // a layout to the given list.
  1104. public int GetUnsupportedDevices(List<InputDeviceDescription> descriptions)
  1105. {
  1106. if (descriptions == null)
  1107. throw new ArgumentNullException(nameof(descriptions));
  1108. var numFound = 0;
  1109. for (var i = 0; i < m_AvailableDeviceCount; ++i)
  1110. {
  1111. if (TryGetDeviceById(m_AvailableDevices[i].deviceId) != null)
  1112. continue;
  1113. descriptions.Add(m_AvailableDevices[i].description);
  1114. ++numFound;
  1115. }
  1116. return numFound;
  1117. }
  1118. ////TODO: this should reset the device to its default state
  1119. public void EnableOrDisableDevice(InputDevice device, bool enable)
  1120. {
  1121. if (device == null)
  1122. throw new ArgumentNullException(nameof(device));
  1123. // Ignore if device already enabled/disabled.
  1124. if (device.enabled == enable)
  1125. return;
  1126. // Set/clear flag.
  1127. if (!enable)
  1128. device.m_DeviceFlags |= InputDevice.DeviceFlags.Disabled;
  1129. else
  1130. device.m_DeviceFlags &= ~InputDevice.DeviceFlags.Disabled;
  1131. // Send command to tell backend about status change.
  1132. if (enable)
  1133. {
  1134. var command = EnableDeviceCommand.Create();
  1135. device.ExecuteCommand(ref command);
  1136. }
  1137. else
  1138. {
  1139. var command = DisableDeviceCommand.Create();
  1140. device.ExecuteCommand(ref command);
  1141. }
  1142. // Let listeners know.
  1143. var deviceChange = enable ? InputDeviceChange.Enabled : InputDeviceChange.Disabled;
  1144. for (var i = 0; i < m_DeviceChangeListeners.length; ++i)
  1145. m_DeviceChangeListeners[i](device, deviceChange);
  1146. }
  1147. ////TODO: support combining monitors for bitfields
  1148. public void AddStateChangeMonitor(InputControl control, IInputStateChangeMonitor monitor, long monitorIndex)
  1149. {
  1150. Debug.Assert(m_DevicesCount > 0);
  1151. var device = control.device;
  1152. var deviceIndex = device.m_DeviceIndex;
  1153. Debug.Assert(deviceIndex != InputDevice.kInvalidDeviceIndex);
  1154. // Allocate/reallocate monitor arrays, if necessary.
  1155. // We lazy-sync it to array of devices.
  1156. if (m_StateChangeMonitors == null)
  1157. m_StateChangeMonitors = new StateChangeMonitorsForDevice[m_DevicesCount];
  1158. else if (m_StateChangeMonitors.Length <= deviceIndex)
  1159. Array.Resize(ref m_StateChangeMonitors, m_DevicesCount);
  1160. // Add record.
  1161. m_StateChangeMonitors[deviceIndex].Add(control, monitor, monitorIndex);
  1162. }
  1163. private void RemoveStateChangeMonitors(InputDevice device)
  1164. {
  1165. if (m_StateChangeMonitors == null)
  1166. return;
  1167. var deviceIndex = device.m_DeviceIndex;
  1168. Debug.Assert(deviceIndex != InputDevice.kInvalidDeviceIndex);
  1169. if (deviceIndex >= m_StateChangeMonitors.Length)
  1170. return;
  1171. m_StateChangeMonitors[deviceIndex].Clear();
  1172. // Clear timeouts pending on any control on the device.
  1173. for (var i = 0; i < m_StateChangeMonitorTimeouts.length; ++i)
  1174. if (m_StateChangeMonitorTimeouts[i].control?.device == device)
  1175. m_StateChangeMonitorTimeouts[i] = default;
  1176. }
  1177. public void RemoveStateChangeMonitor(InputControl control, IInputStateChangeMonitor monitor, long monitorIndex)
  1178. {
  1179. if (m_StateChangeMonitors == null)
  1180. return;
  1181. var device = control.device;
  1182. var deviceIndex = device.m_DeviceIndex;
  1183. // Ignore if device has already been removed.
  1184. if (deviceIndex == InputDevice.kInvalidDeviceIndex)
  1185. return;
  1186. // Ignore if there are no state monitors set up for the device.
  1187. if (deviceIndex >= m_StateChangeMonitors.Length)
  1188. return;
  1189. m_StateChangeMonitors[deviceIndex].Remove(monitor, monitorIndex);
  1190. // Remove pending timeouts on the monitor.
  1191. for (var i = 0; i < m_StateChangeMonitorTimeouts.length; ++i)
  1192. if (m_StateChangeMonitorTimeouts[i].monitor == monitor &&
  1193. m_StateChangeMonitorTimeouts[i].monitorIndex == monitorIndex)
  1194. m_StateChangeMonitorTimeouts[i] = default;
  1195. }
  1196. public void AddStateChangeMonitorTimeout(InputControl control, IInputStateChangeMonitor monitor, double time, long monitorIndex, int timerIndex)
  1197. {
  1198. m_StateChangeMonitorTimeouts.Append(
  1199. new StateChangeMonitorTimeout
  1200. {
  1201. control = control,
  1202. time = time,
  1203. monitor = monitor,
  1204. monitorIndex = monitorIndex,
  1205. timerIndex = timerIndex,
  1206. });
  1207. }
  1208. public void RemoveStateChangeMonitorTimeout(IInputStateChangeMonitor monitor, long monitorIndex, int timerIndex)
  1209. {
  1210. var timeoutCount = m_StateChangeMonitorTimeouts.length;
  1211. for (var i = 0; i < timeoutCount; ++i)
  1212. {
  1213. ////REVIEW: can we avoid the repeated array lookups without copying the struct out?
  1214. if (ReferenceEquals(m_StateChangeMonitorTimeouts[i].monitor, monitor)
  1215. && m_StateChangeMonitorTimeouts[i].monitorIndex == monitorIndex
  1216. && m_StateChangeMonitorTimeouts[i].timerIndex == timerIndex)
  1217. {
  1218. m_StateChangeMonitorTimeouts[i] = default;
  1219. break;
  1220. }
  1221. }
  1222. }
  1223. public unsafe void QueueEvent(InputEventPtr ptr)
  1224. {
  1225. m_Runtime.QueueEvent(ptr.data);
  1226. }
  1227. public unsafe void QueueEvent<TEvent>(ref TEvent inputEvent)
  1228. where TEvent : struct, IInputEventTypeInfo
  1229. {
  1230. // Don't bother keeping the data on the managed side. Just stuff the raw data directly
  1231. // into the native buffers. This also means this method is thread-safe.
  1232. m_Runtime.QueueEvent((InputEvent*)UnsafeUtility.AddressOf(ref inputEvent));
  1233. }
  1234. public void Update()
  1235. {
  1236. Update(defaultUpdateType);
  1237. }
  1238. public void Update(InputUpdateType updateType)
  1239. {
  1240. m_Runtime.Update(updateType);
  1241. }
  1242. internal void Initialize(IInputRuntime runtime, InputSettings settings)
  1243. {
  1244. Debug.Assert(settings != null);
  1245. m_Settings = settings;
  1246. InitializeData();
  1247. InstallRuntime(runtime);
  1248. InstallGlobals();
  1249. ApplySettings();
  1250. }
  1251. internal void Destroy()
  1252. {
  1253. // There isn't really much of a point in removing devices but we still
  1254. // want to clear out any global state they may be keeping. So just tell
  1255. // the devices that they got removed without actually removing them.
  1256. for (var i = 0; i < m_DevicesCount; ++i)
  1257. m_Devices[i].NotifyRemoved();
  1258. // Free all state memory.
  1259. m_StateBuffers.FreeAll();
  1260. // Uninstall globals.
  1261. UninstallGlobals();
  1262. // Destroy settings if they are temporary.
  1263. if (m_Settings != null && m_Settings.hideFlags == HideFlags.HideAndDontSave)
  1264. Object.DestroyImmediate(m_Settings);
  1265. }
  1266. internal void InitializeData()
  1267. {
  1268. m_Layouts.Allocate();
  1269. m_Processors.Initialize();
  1270. m_Interactions.Initialize();
  1271. m_Composites.Initialize();
  1272. m_DevicesById = new Dictionary<int, InputDevice>();
  1273. // Determine our default set of enabled update types. By
  1274. // default we enable both fixed and dynamic update because
  1275. // we don't know which one the user is going to use. The user
  1276. // can manually turn off one of them to optimize operation.
  1277. m_UpdateMask = InputUpdateType.Dynamic | InputUpdateType.Fixed;
  1278. m_HasFocus = Application.isFocused;
  1279. #if UNITY_EDITOR
  1280. m_UpdateMask |= InputUpdateType.Editor;
  1281. #endif
  1282. // Default polling frequency is 60 Hz.
  1283. m_PollingFrequency = 60;
  1284. // Register layouts.
  1285. RegisterControlLayout("Axis", typeof(AxisControl)); // Controls.
  1286. RegisterControlLayout("Button", typeof(ButtonControl));
  1287. RegisterControlLayout("DiscreteButton", typeof(DiscreteButtonControl));
  1288. RegisterControlLayout("Key", typeof(KeyControl));
  1289. RegisterControlLayout("Analog", typeof(AxisControl));
  1290. RegisterControlLayout("Integer", typeof(IntegerControl));
  1291. RegisterControlLayout("Digital", typeof(IntegerControl));
  1292. RegisterControlLayout("Double", typeof(DoubleControl));
  1293. RegisterControlLayout("Vector2", typeof(Vector2Control));
  1294. RegisterControlLayout("Vector3", typeof(Vector3Control));
  1295. RegisterControlLayout("Quaternion", typeof(QuaternionControl));
  1296. RegisterControlLayout("Stick", typeof(StickControl));
  1297. RegisterControlLayout("Dpad", typeof(DpadControl));
  1298. RegisterControlLayout("DpadAxis", typeof(DpadControl.DpadAxisControl));
  1299. RegisterControlLayout("AnyKey", typeof(AnyKeyControl));
  1300. RegisterControlLayout("Touch", typeof(TouchControl));
  1301. RegisterControlLayout("TouchPhase", typeof(TouchPhaseControl));
  1302. RegisterControlLayout("TouchPress", typeof(TouchPressControl));
  1303. RegisterControlLayout("Gamepad", typeof(Gamepad)); // Devices.
  1304. RegisterControlLayout("Joystick", typeof(Joystick));
  1305. RegisterControlLayout("Keyboard", typeof(Keyboard));
  1306. RegisterControlLayout("Pointer", typeof(Pointer));
  1307. RegisterControlLayout("Mouse", typeof(Mouse));
  1308. RegisterControlLayout("Pen", typeof(Pen));
  1309. RegisterControlLayout("Touchscreen", typeof(Touchscreen));
  1310. RegisterControlLayout("Sensor", typeof(Sensor));
  1311. RegisterControlLayout("Accelerometer", typeof(Accelerometer));
  1312. RegisterControlLayout("Gyroscope", typeof(Gyroscope));
  1313. RegisterControlLayout("GravitySensor", typeof(GravitySensor));
  1314. RegisterControlLayout("AttitudeSensor", typeof(AttitudeSensor));
  1315. RegisterControlLayout("LinearAccelerationSensor", typeof(LinearAccelerationSensor));
  1316. RegisterControlLayout("MagneticFieldSensor", typeof(MagneticFieldSensor));
  1317. RegisterControlLayout("LightSensor", typeof(LightSensor));
  1318. RegisterControlLayout("PressureSensor", typeof(PressureSensor));
  1319. RegisterControlLayout("HumiditySensor", typeof(HumiditySensor));
  1320. RegisterControlLayout("AmbientTemperatureSensor", typeof(AmbientTemperatureSensor));
  1321. RegisterControlLayout("StepCounter", typeof(StepCounter));
  1322. RegisterControlLayout("TrackedDevice", typeof(TrackedDevice));
  1323. // Register processors.
  1324. processors.AddTypeRegistration("Invert", typeof(InvertProcessor));
  1325. processors.AddTypeRegistration("InvertVector2", typeof(InvertVector2Processor));
  1326. processors.AddTypeRegistration("InvertVector3", typeof(InvertVector3Processor));
  1327. processors.AddTypeRegistration("Clamp", typeof(ClampProcessor));
  1328. processors.AddTypeRegistration("Normalize", typeof(NormalizeProcessor));
  1329. processors.AddTypeRegistration("NormalizeVector2", typeof(NormalizeVector2Processor));
  1330. processors.AddTypeRegistration("NormalizeVector3", typeof(NormalizeVector3Processor));
  1331. processors.AddTypeRegistration("Scale", typeof(ScaleProcessor));
  1332. processors.AddTypeRegistration("ScaleVector2", typeof(ScaleVector2Processor));
  1333. processors.AddTypeRegistration("ScaleVector3", typeof(ScaleVector3Processor));
  1334. processors.AddTypeRegistration("StickDeadzone", typeof(StickDeadzoneProcessor));
  1335. processors.AddTypeRegistration("AxisDeadzone", typeof(AxisDeadzoneProcessor));
  1336. processors.AddTypeRegistration("CompensateDirection", typeof(CompensateDirectionProcessor));
  1337. processors.AddTypeRegistration("CompensateRotation", typeof(CompensateRotationProcessor));
  1338. #if UNITY_EDITOR
  1339. processors.AddTypeRegistration("AutoWindowSpace", typeof(EditorWindowSpaceProcessor));
  1340. #endif
  1341. // Register interactions.
  1342. interactions.AddTypeRegistration("Hold", typeof(HoldInteraction));
  1343. interactions.AddTypeRegistration("Tap", typeof(TapInteraction));
  1344. interactions.AddTypeRegistration("SlowTap", typeof(SlowTapInteraction));
  1345. interactions.AddTypeRegistration("MultiTap", typeof(MultiTapInteraction));
  1346. interactions.AddTypeRegistration("Press", typeof(PressInteraction));
  1347. // Register composites.
  1348. composites.AddTypeRegistration("1DAxis", typeof(AxisComposite));
  1349. composites.AddTypeRegistration("2DVector", typeof(Vector2Composite));
  1350. composites.AddTypeRegistration("Axis", typeof(AxisComposite));// Alias for pre-0.2 name.
  1351. composites.AddTypeRegistration("Dpad", typeof(Vector2Composite));// Alias for pre-0.2 name.
  1352. composites.AddTypeRegistration("ButtonWithOneModifier", typeof(ButtonWithOneModifier));
  1353. composites.AddTypeRegistration("ButtonWithTwoModifiers", typeof(ButtonWithTwoModifiers));
  1354. }
  1355. internal void InstallRuntime(IInputRuntime runtime)
  1356. {
  1357. if (m_Runtime != null)
  1358. {
  1359. m_Runtime.onUpdate = null;
  1360. m_Runtime.onBeforeUpdate = null;
  1361. m_Runtime.onDeviceDiscovered = null;
  1362. m_Runtime.onPlayerFocusChanged = null;
  1363. m_Runtime.onShouldRunUpdate = null;
  1364. }
  1365. m_Runtime = runtime;
  1366. m_Runtime.onUpdate = OnUpdate;
  1367. m_Runtime.onDeviceDiscovered = OnNativeDeviceDiscovered;
  1368. m_Runtime.onPlayerFocusChanged = OnFocusChanged;
  1369. m_Runtime.onShouldRunUpdate = ShouldRunUpdate;
  1370. m_Runtime.pollingFrequency = pollingFrequency;
  1371. // We only hook NativeInputSystem.onBeforeUpdate if necessary.
  1372. if (m_BeforeUpdateListeners.length > 0 || m_HaveDevicesWithStateCallbackReceivers)
  1373. {
  1374. m_Runtime.onBeforeUpdate = OnBeforeUpdate;
  1375. m_NativeBeforeUpdateHooked = true;
  1376. }
  1377. #if UNITY_ANALYTICS || UNITY_EDITOR
  1378. InputAnalytics.Initialize(this);
  1379. m_Runtime.onShutdown = () => InputAnalytics.OnShutdown(this);
  1380. #endif
  1381. }
  1382. internal void InstallGlobals()
  1383. {
  1384. Debug.Assert(m_Runtime != null);
  1385. InputControlLayout.s_Layouts = m_Layouts;
  1386. InputProcessor.s_Processors = m_Processors;
  1387. InputInteraction.s_Interactions = m_Interactions;
  1388. InputBindingComposite.s_Composites = m_Composites;
  1389. InputRuntime.s_Instance = m_Runtime;
  1390. InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup =
  1391. m_Runtime.currentTimeOffsetToRealtimeSinceStartup;
  1392. // Reset update state.
  1393. InputUpdate.Restore(new InputUpdate.SerializedState());
  1394. unsafe
  1395. {
  1396. InputStateBuffers.SwitchTo(m_StateBuffers, InputUpdateType.Dynamic);
  1397. InputStateBuffers.s_DefaultStateBuffer = m_StateBuffers.defaultStateBuffer;
  1398. InputStateBuffers.s_NoiseMaskBuffer = m_StateBuffers.noiseMaskBuffer;
  1399. }
  1400. }
  1401. internal void UninstallGlobals()
  1402. {
  1403. if (ReferenceEquals(InputControlLayout.s_Layouts.baseLayoutTable, m_Layouts.baseLayoutTable))
  1404. InputControlLayout.s_Layouts = new InputControlLayout.Collection();
  1405. if (ReferenceEquals(InputProcessor.s_Processors.table, m_Processors.table))
  1406. InputProcessor.s_Processors = new TypeTable();
  1407. if (ReferenceEquals(InputInteraction.s_Interactions.table, m_Interactions.table))
  1408. InputInteraction.s_Interactions = new TypeTable();
  1409. if (ReferenceEquals(InputBindingComposite.s_Composites.table, m_Composites.table))
  1410. InputBindingComposite.s_Composites = new TypeTable();
  1411. // Clear layout cache.
  1412. InputControlLayout.s_CacheInstance = default;
  1413. InputControlLayout.s_CacheInstanceRef = 0;
  1414. // Detach from runtime.
  1415. if (m_Runtime != null)
  1416. {
  1417. m_Runtime.onUpdate = null;
  1418. m_Runtime.onDeviceDiscovered = null;
  1419. m_Runtime.onBeforeUpdate = null;
  1420. m_Runtime.onPlayerFocusChanged = null;
  1421. m_Runtime.onShouldRunUpdate = null;
  1422. if (ReferenceEquals(InputRuntime.s_Instance, m_Runtime))
  1423. InputRuntime.s_Instance = null;
  1424. }
  1425. }
  1426. [Serializable]
  1427. internal struct AvailableDevice
  1428. {
  1429. public InputDeviceDescription description;
  1430. public int deviceId;
  1431. public bool isNative;
  1432. public bool isRemoved;
  1433. }
  1434. // Used by EditorInputControlLayoutCache to determine whether its state is outdated.
  1435. internal int m_LayoutRegistrationVersion;
  1436. private float m_PollingFrequency;
  1437. internal InputControlLayout.Collection m_Layouts;
  1438. private TypeTable m_Processors;
  1439. private TypeTable m_Interactions;
  1440. private TypeTable m_Composites;
  1441. private int m_DevicesCount;
  1442. private InputDevice[] m_Devices;
  1443. private Dictionary<int, InputDevice> m_DevicesById;
  1444. internal int m_AvailableDeviceCount;
  1445. internal AvailableDevice[] m_AvailableDevices; // A record of all devices reported to the system (from native or user code).
  1446. ////REVIEW: should these be weak-referenced?
  1447. internal int m_DisconnectedDevicesCount;
  1448. internal InputDevice[] m_DisconnectedDevices;
  1449. private InputUpdateType m_UpdateMask; // Which of our update types are enabled.
  1450. internal InputStateBuffers m_StateBuffers;
  1451. // We don't use UnityEvents and thus don't persist the callbacks during domain reloads.
  1452. // Restoration of UnityActions is unreliable and it's too easy to end up with double
  1453. // registrations what will lead to all kinds of misbehavior.
  1454. private InlinedArray<DeviceChangeListener> m_DeviceChangeListeners;
  1455. private InlinedArray<DeviceStateChangeListener> m_DeviceStateChangeListeners;
  1456. private InlinedArray<InputDeviceFindControlLayoutDelegate> m_DeviceFindLayoutCallbacks;
  1457. internal InlinedArray<InputDeviceCommandDelegate> m_DeviceCommandCallbacks;
  1458. private InlinedArray<LayoutChangeListener> m_LayoutChangeListeners;
  1459. private InlinedArray<EventListener> m_EventListeners;
  1460. private InlinedArray<UpdateListener> m_BeforeUpdateListeners;
  1461. private InlinedArray<UpdateListener> m_AfterUpdateListeners;
  1462. private InlinedArray<Action> m_SettingsChangedListeners;
  1463. private bool m_NativeBeforeUpdateHooked;
  1464. private bool m_HaveDevicesWithStateCallbackReceivers;
  1465. private bool m_HasFocus;
  1466. // We allocate the 'executeDeviceCommand' closure passed to 'onFindLayoutForDevice'
  1467. // only once to avoid creating garbage.
  1468. private InputDeviceExecuteCommandDelegate m_DeviceFindExecuteCommandDelegate;
  1469. private int m_DeviceFindExecuteCommandDeviceId;
  1470. #if UNITY_ANALYTICS || UNITY_EDITOR
  1471. private bool m_HaveSentStartupAnalytics;
  1472. #endif
  1473. internal IInputRuntime m_Runtime;
  1474. internal InputMetrics m_Metrics;
  1475. internal InputSettings m_Settings;
  1476. #if UNITY_EDITOR
  1477. internal IInputDiagnostics m_Diagnostics;
  1478. #endif
  1479. // Maps a single control to an action interested in the control. If
  1480. // multiple actions are interested in the same control, we will end up
  1481. // processing the control repeatedly but we assume this is the exception
  1482. // and so optimize for the case where there's only one action going to
  1483. // a control.
  1484. //
  1485. // Split into two structures to keep data needed only when there is an
  1486. // actual value change out of the data we need for doing the scanning.
  1487. internal struct StateChangeMonitorListener
  1488. {
  1489. public InputControl control;
  1490. public IInputStateChangeMonitor monitor;
  1491. public long monitorIndex;
  1492. }
  1493. internal struct StateChangeMonitorsForDevice
  1494. {
  1495. public MemoryHelpers.BitRegion[] memoryRegions;
  1496. public StateChangeMonitorListener[] listeners;
  1497. public DynamicBitfield signalled;
  1498. public int count => signalled.length;
  1499. public void Add(InputControl control, IInputStateChangeMonitor monitor, long monitorIndex)
  1500. {
  1501. // NOTE: This method must only *append* to arrays. This way we can safely add data while traversing
  1502. // the arrays in FireStateChangeNotifications. Note that appending *may* mean that the arrays
  1503. // are switched to larger arrays.
  1504. // Record listener.
  1505. var listenerCount = signalled.length;
  1506. ArrayHelpers.AppendWithCapacity(ref listeners, ref listenerCount,
  1507. new StateChangeMonitorListener {monitor = monitor, monitorIndex = monitorIndex, control = control});
  1508. // Record memory region.
  1509. ref var controlStateBlock = ref control.m_StateBlock;
  1510. var memoryRegionCount = signalled.length;
  1511. ArrayHelpers.AppendWithCapacity(ref memoryRegions, ref memoryRegionCount,
  1512. new MemoryHelpers.BitRegion(controlStateBlock.byteOffset - control.device.stateBlock.byteOffset,
  1513. controlStateBlock.bitOffset, controlStateBlock.sizeInBits));
  1514. signalled.SetLength(signalled.length + 1);
  1515. }
  1516. public void Remove(IInputStateChangeMonitor monitor, long monitorIndex)
  1517. {
  1518. // NOTE: This must *not* actually destroy the record for the monitor as we may currently be traversing the
  1519. // arrays in FireStateChangeNotifications. Instead, we only invalidate entries here and leave it to
  1520. // ProcessStateChangeMonitors to compact arrays.
  1521. if (listeners == null)
  1522. return;
  1523. for (var i = 0; i < signalled.length; ++i)
  1524. if (ReferenceEquals(listeners[i].monitor, monitor) && listeners[i].monitorIndex == monitorIndex)
  1525. {
  1526. listeners[i] = default;
  1527. memoryRegions[i] = default;
  1528. signalled.ClearBit(i);
  1529. break;
  1530. }
  1531. }
  1532. public void Clear()
  1533. {
  1534. // We don't actually release memory we've potentially allocated but rather just reset
  1535. // our count to zero.
  1536. listeners.Clear(count);
  1537. signalled.SetLength(0);
  1538. }
  1539. }
  1540. // Indices correspond with those in m_Devices.
  1541. internal StateChangeMonitorsForDevice[] m_StateChangeMonitors;
  1542. /// <summary>
  1543. /// Record for a timeout installed on a state change monitor.
  1544. /// </summary>
  1545. private struct StateChangeMonitorTimeout
  1546. {
  1547. public InputControl control;
  1548. public double time;
  1549. public IInputStateChangeMonitor monitor;
  1550. public long monitorIndex;
  1551. public int timerIndex;
  1552. }
  1553. private InlinedArray<StateChangeMonitorTimeout> m_StateChangeMonitorTimeouts;
  1554. ////REVIEW: Make it so that device names *always* have a number appended? (i.e. Gamepad1, Gamepad2, etc. instead of Gamepad, Gamepad1, etc)
  1555. private void MakeDeviceNameUnique(InputDevice device)
  1556. {
  1557. if (m_DevicesCount == 0)
  1558. return;
  1559. var deviceName = StringHelpers.MakeUniqueName(device.name, m_Devices, x => x != null ? x.name : string.Empty);
  1560. if (deviceName != device.name)
  1561. {
  1562. // If we have changed the name of the device, nuke all path strings in the control
  1563. // hierarchy so that they will get re-recreated when queried.
  1564. ResetControlPathsRecursive(device);
  1565. // Assign name.
  1566. device.m_Name = new InternedString(deviceName);
  1567. }
  1568. }
  1569. private static void ResetControlPathsRecursive(InputControl control)
  1570. {
  1571. control.m_Path = null;
  1572. var children = control.children;
  1573. var childCount = children.Count;
  1574. for (var i = 0; i < childCount; ++i)
  1575. ResetControlPathsRecursive(children[i]);
  1576. }
  1577. private void AssignUniqueDeviceId(InputDevice device)
  1578. {
  1579. // If the device already has an ID, make sure it's unique.
  1580. if (device.deviceId != InputDevice.InvalidDeviceId)
  1581. {
  1582. // Safety check to make sure out IDs are really unique.
  1583. // Given they are assigned by the native system they should be fine
  1584. // but let's make sure.
  1585. var existingDeviceWithId = TryGetDeviceById(device.deviceId);
  1586. if (existingDeviceWithId != null)
  1587. throw new InvalidOperationException(
  1588. $"Duplicate device ID {device.deviceId} detected for devices '{device.name}' and '{existingDeviceWithId.name}'");
  1589. }
  1590. else
  1591. {
  1592. device.m_DeviceId = m_Runtime.AllocateDeviceId();
  1593. }
  1594. }
  1595. // (Re)allocates state buffers and assigns each device that's been added
  1596. // a segment of the buffer. Preserves the current state of devices.
  1597. // NOTE: Installs the buffers globally.
  1598. private unsafe void ReallocateStateBuffers()
  1599. {
  1600. var oldBuffers = m_StateBuffers;
  1601. // Allocate new buffers.
  1602. var newBuffers = new InputStateBuffers();
  1603. newBuffers.AllocateAll(m_Devices, m_DevicesCount);
  1604. // Migrate state.
  1605. newBuffers.MigrateAll(m_Devices, m_DevicesCount, oldBuffers);
  1606. // Install the new buffers.
  1607. oldBuffers.FreeAll();
  1608. m_StateBuffers = newBuffers;
  1609. InputStateBuffers.s_DefaultStateBuffer = newBuffers.defaultStateBuffer;
  1610. InputStateBuffers.s_NoiseMaskBuffer = newBuffers.noiseMaskBuffer;
  1611. // Switch to buffers.
  1612. InputStateBuffers.SwitchTo(m_StateBuffers,
  1613. InputUpdate.s_LastUpdateType != InputUpdateType.None ? InputUpdate.s_LastUpdateType : defaultUpdateType);
  1614. ////TODO: need to update state change monitors
  1615. }
  1616. /// <summary>
  1617. /// Initialize default state for given device.
  1618. /// </summary>
  1619. /// <param name="device">A newly added input device.</param>
  1620. /// <remarks>
  1621. /// For every device, one copy of its state is kept around which is initialized with the default
  1622. /// values for the device. If the device has no control that has an explicitly specified control
  1623. /// value, the buffer simply contains all zeroes.
  1624. ///
  1625. /// The default state buffer is initialized once when a device is added to the system and then
  1626. /// migrated by <see cref="InputStateBuffers"/> like other device state and removed when the device
  1627. /// is removed from the system.
  1628. /// </remarks>
  1629. private unsafe void InitializeDefaultState(InputDevice device)
  1630. {
  1631. // Nothing to do if device has a default state of all zeroes.
  1632. if (!device.hasControlsWithDefaultState)
  1633. return;
  1634. // Otherwise go through each control and write its default value.
  1635. var controls = device.allControls;
  1636. var controlCount = controls.Count;
  1637. var defaultStateBuffer = m_StateBuffers.defaultStateBuffer;
  1638. for (var n = 0; n < controlCount; ++n)
  1639. {
  1640. var control = controls[n];
  1641. if (!control.hasDefaultState)
  1642. continue;
  1643. control.m_StateBlock.Write(defaultStateBuffer, control.m_DefaultState);
  1644. }
  1645. // Copy default state to all front and back buffers.
  1646. var stateBlock = device.m_StateBlock;
  1647. var deviceIndex = device.m_DeviceIndex;
  1648. if (m_StateBuffers.m_PlayerStateBuffers.valid)
  1649. {
  1650. stateBlock.CopyToFrom(m_StateBuffers.m_PlayerStateBuffers.GetFrontBuffer(deviceIndex), defaultStateBuffer);
  1651. stateBlock.CopyToFrom(m_StateBuffers.m_PlayerStateBuffers.GetBackBuffer(deviceIndex), defaultStateBuffer);
  1652. }
  1653. #if UNITY_EDITOR
  1654. if (m_StateBuffers.m_EditorStateBuffers.valid)
  1655. {
  1656. stateBlock.CopyToFrom(m_StateBuffers.m_EditorStateBuffers.GetFrontBuffer(deviceIndex), defaultStateBuffer);
  1657. stateBlock.CopyToFrom(m_StateBuffers.m_EditorStateBuffers.GetBackBuffer(deviceIndex), defaultStateBuffer);
  1658. }
  1659. #endif
  1660. }
  1661. private unsafe void InitializeNoiseMask(InputDevice device)
  1662. {
  1663. Debug.Assert(device != null, "Device must not be null");
  1664. Debug.Assert(device.added, "Device must have been added");
  1665. Debug.Assert(device.stateBlock.byteOffset != InputStateBlock.InvalidOffset, "Device state block offset is invalid");
  1666. Debug.Assert(
  1667. device.stateBlock.byteOffset + device.stateBlock.alignedSizeInBytes <= m_StateBuffers.sizePerBuffer,
  1668. "Device state block is not contained in state buffer");
  1669. var controls = device.allControls;
  1670. var controlCount = controls.Count;
  1671. // Assume that everything in the device is noise. This way we also catch memory regions
  1672. // that are not actually covered by a control and implicitly mark them as noise (e.g. the
  1673. // report ID in HID input reports).
  1674. //
  1675. // NOTE: Noise is indicated by *unset* bits so we don't have to do anything here to start
  1676. // with all-noise as we expect noise mask memory to be cleared on allocation.
  1677. var noiseMaskBuffer = m_StateBuffers.noiseMaskBuffer;
  1678. ////FIXME: this needs to properly take leaf vs non-leaf controls into account
  1679. // Go through controls and for each one that isn't noisy, set the control's
  1680. // bits in the mask.
  1681. for (var n = 0; n < controlCount; ++n)
  1682. {
  1683. var control = controls[n];
  1684. if (control.noisy)
  1685. continue;
  1686. ref var stateBlock = ref control.m_StateBlock;
  1687. Debug.Assert(stateBlock.byteOffset != InputStateBlock.InvalidOffset, "Byte offset is invalid on control's state block");
  1688. Debug.Assert(stateBlock.bitOffset != InputStateBlock.InvalidOffset, "Bit offset is invalid on control's state block");
  1689. Debug.Assert(stateBlock.sizeInBits != InputStateBlock.InvalidOffset, "Size is invalid on control's state block");
  1690. Debug.Assert(stateBlock.byteOffset >= device.stateBlock.byteOffset, "Control's offset is located below device's offset");
  1691. Debug.Assert(stateBlock.byteOffset + stateBlock.alignedSizeInBytes <=
  1692. device.stateBlock.byteOffset + device.stateBlock.alignedSizeInBytes, "Control state block lies outside of state buffer");
  1693. MemoryHelpers.SetBitsInBuffer(noiseMaskBuffer, (int)stateBlock.byteOffset, (int)stateBlock.bitOffset,
  1694. (int)stateBlock.sizeInBits, true);
  1695. }
  1696. }
  1697. private void OnNativeDeviceDiscovered(int deviceId, string deviceDescriptor)
  1698. {
  1699. // Make sure we're not adding to m_AvailableDevices before we restored what we
  1700. // had before a domain reload.
  1701. RestoreDevicesAfterDomainReloadIfNecessary();
  1702. // See if we have a disconnected device we can revive.
  1703. // NOTE: We do this all the way up here as the first thing before we even parse the JSON descriptor so
  1704. // if we do have a device we can revive, we can do so without allocating any GC memory.
  1705. var device = TryMatchDisconnectedDevice(deviceDescriptor);
  1706. // Parse description, if need be.
  1707. var description = device?.description ?? InputDeviceDescription.FromJson(deviceDescriptor);
  1708. // Add it.
  1709. var markAsRemoved = false;
  1710. try
  1711. {
  1712. // If we have a restricted set of supported devices, first check if it's a device
  1713. // we should support.
  1714. if (m_Settings.supportedDevices.Count > 0)
  1715. {
  1716. var layout = device != null ? device.m_Layout : TryFindMatchingControlLayout(ref description, deviceId);
  1717. if (!IsDeviceLayoutMarkedAsSupportedInSettings(layout))
  1718. {
  1719. // Not supported. Ignore device. Still will get added to m_AvailableDevices
  1720. // list in finally clause below. If later the set of supported devices changes
  1721. // so that the device is now supported, ApplySettings() will pull it back out
  1722. // and create the device.
  1723. markAsRemoved = true;
  1724. return;
  1725. }
  1726. }
  1727. if (device != null)
  1728. {
  1729. // It's a device we pulled from the disconnected list. Update the device with the
  1730. // new ID, re-add it and notify that we've reconnected.
  1731. device.m_DeviceId = deviceId;
  1732. AddDevice(device);
  1733. for (var i = 0; i < m_DeviceChangeListeners.length; ++i)
  1734. m_DeviceChangeListeners[i](device, InputDeviceChange.Reconnected);
  1735. }
  1736. else
  1737. {
  1738. // Go through normal machinery to try to create a new device.
  1739. AddDevice(description, throwIfNoLayoutFound: false, deviceId: deviceId,
  1740. deviceFlags: InputDevice.DeviceFlags.Native);
  1741. }
  1742. }
  1743. // We're catching exceptions very aggressively here. The reason is that we don't want
  1744. // exceptions thrown as a result of trying to create devices from device discoveries reported
  1745. // by native to break the system as a whole. Instead, we want to make the error visible but then
  1746. // go and work with whatever devices we *did* manage to create successfully.
  1747. catch (Exception exception)
  1748. {
  1749. Debug.LogError($"Could not create a device for '{description}' (exception: {exception})");
  1750. }
  1751. finally
  1752. {
  1753. // Remember it. Do this *after* the AddDevice() call above so that if there's
  1754. // a listener creating layouts on the fly we won't end up matching this device and
  1755. // create an InputDevice right away (which would then conflict with the one we
  1756. // create in AddDevice).
  1757. ArrayHelpers.AppendWithCapacity(ref m_AvailableDevices, ref m_AvailableDeviceCount,
  1758. new AvailableDevice
  1759. {
  1760. description = description,
  1761. deviceId = deviceId,
  1762. isNative = true,
  1763. isRemoved = markAsRemoved,
  1764. });
  1765. }
  1766. }
  1767. private InputDevice TryMatchDisconnectedDevice(string deviceDescriptor)
  1768. {
  1769. for (var i = 0; i < m_DisconnectedDevicesCount; ++i)
  1770. {
  1771. var device = m_DisconnectedDevices[i];
  1772. var description = device.description;
  1773. // We don't parse the full description but rather go property by property in order to not
  1774. // allocate GC memory if we can avoid it.
  1775. if (!string.IsNullOrEmpty(description.interfaceName) &&
  1776. !InputDeviceDescription.ComparePropertyToDeviceDescriptor("interface", description.interfaceName, deviceDescriptor))
  1777. continue;
  1778. if (!string.IsNullOrEmpty(description.product) &&
  1779. !InputDeviceDescription.ComparePropertyToDeviceDescriptor("product", description.product, deviceDescriptor))
  1780. continue;
  1781. if (!string.IsNullOrEmpty(description.manufacturer) &&
  1782. !InputDeviceDescription.ComparePropertyToDeviceDescriptor("manufacturer", description.manufacturer, deviceDescriptor))
  1783. continue;
  1784. if (!string.IsNullOrEmpty(description.deviceClass) &&
  1785. !InputDeviceDescription.ComparePropertyToDeviceDescriptor("type", description.deviceClass, deviceDescriptor))
  1786. continue;
  1787. // We ignore capabilities here.
  1788. ArrayHelpers.EraseAtWithCapacity(m_DisconnectedDevices, ref m_DisconnectedDevicesCount, i);
  1789. return device;
  1790. }
  1791. return null;
  1792. }
  1793. private void InstallBeforeUpdateHookIfNecessary()
  1794. {
  1795. if (m_NativeBeforeUpdateHooked || m_Runtime == null)
  1796. return;
  1797. m_Runtime.onBeforeUpdate = OnBeforeUpdate;
  1798. m_NativeBeforeUpdateHooked = true;
  1799. }
  1800. private void RestoreDevicesAfterDomainReloadIfNecessary()
  1801. {
  1802. #if UNITY_EDITOR
  1803. if (m_SavedDeviceStates != null)
  1804. RestoreDevicesAfterDomainReload();
  1805. #endif
  1806. }
  1807. private void WarnAboutDevicesFailingToRecreateAfterDomainReload()
  1808. {
  1809. // If we still have any saved device states, we have devices that we couldn't figure
  1810. // out how to recreate after a domain reload. Log a warning for each of them and
  1811. // let go of them.
  1812. #if UNITY_EDITOR
  1813. if (m_SavedDeviceStates == null)
  1814. return;
  1815. for (var i = 0; i < m_SavedDeviceStates.Length; ++i)
  1816. {
  1817. ref var state = ref m_SavedDeviceStates[i];
  1818. Debug.LogWarning($"Could not recreate device '{state.name}' with layout '{state.layout}' after domain reload");
  1819. }
  1820. // At this point, we throw the device states away and forget about
  1821. // what we had before the domain reload.
  1822. m_SavedDeviceStates = null;
  1823. #endif
  1824. }
  1825. private void OnBeforeUpdate(InputUpdateType updateType)
  1826. {
  1827. // Restore devices before checking update mask. See InputSystem.RunInitialUpdate().
  1828. RestoreDevicesAfterDomainReloadIfNecessary();
  1829. if ((updateType & m_UpdateMask) == 0)
  1830. return;
  1831. InputStateBuffers.SwitchTo(m_StateBuffers, updateType);
  1832. // For devices that have state callbacks, tell them we're carrying state over
  1833. // into the next frame.
  1834. if (m_HaveDevicesWithStateCallbackReceivers && updateType != InputUpdateType.BeforeRender) ////REVIEW: before-render handling is probably wrong
  1835. {
  1836. ////TODO: have to handle updatecount here, too
  1837. InputUpdate.s_LastUpdateType = updateType;
  1838. for (var i = 0; i < m_DevicesCount; ++i)
  1839. {
  1840. var device = m_Devices[i];
  1841. if ((device.m_DeviceFlags & InputDevice.DeviceFlags.HasStateCallbacks) == 0)
  1842. continue;
  1843. // NOTE: We do *not* perform a buffer flip here as we do not want to change what is the
  1844. // current and what is the previous state when we carry state forward. Rather,
  1845. // OnBeforeUpdate, if it modifies state, it modifies the current state directly.
  1846. // Also, for the same reasons, we do not modify the dynamic/fixed update counts
  1847. // on the device. If an event comes in in the upcoming update, it should lead to
  1848. // a buffer flip.
  1849. ((IInputStateCallbackReceiver)device).OnNextUpdate();
  1850. }
  1851. }
  1852. DelegateHelpers.InvokeCallbacksSafe(ref m_BeforeUpdateListeners, "onBeforeUpdate");
  1853. }
  1854. /// <summary>
  1855. /// Apply the settings in <see cref="m_Settings"/>.
  1856. /// </summary>
  1857. internal void ApplySettings()
  1858. {
  1859. // Sync update mask.
  1860. var newUpdateMask = InputUpdateType.Editor;
  1861. if ((m_UpdateMask & InputUpdateType.BeforeRender) != 0)
  1862. {
  1863. // BeforeRender updates are enabled in response to devices needing BeforeRender updates
  1864. // so we always preserve this if set.
  1865. newUpdateMask |= InputUpdateType.BeforeRender;
  1866. }
  1867. if (m_Settings.updateMode == InputSettings.s_OldUnsupportedFixedAndDynamicUpdateSetting)
  1868. m_Settings.updateMode = InputSettings.UpdateMode.ProcessEventsInDynamicUpdate;
  1869. switch (m_Settings.updateMode)
  1870. {
  1871. case InputSettings.UpdateMode.ProcessEventsInDynamicUpdate:
  1872. newUpdateMask |= InputUpdateType.Dynamic;
  1873. break;
  1874. case InputSettings.UpdateMode.ProcessEventsInFixedUpdate:
  1875. newUpdateMask |= InputUpdateType.Fixed;
  1876. break;
  1877. case InputSettings.UpdateMode.ProcessEventsManually:
  1878. newUpdateMask |= InputUpdateType.Manual;
  1879. break;
  1880. default:
  1881. throw new NotSupportedException("Invalid input update mode: " + m_Settings.updateMode);
  1882. }
  1883. #if UNITY_EDITOR
  1884. // In the editor, we force editor updates to be on even if InputEditorUserSettings.lockInputToGameView is
  1885. // on as otherwise we'll end up accumulating events in edit mode without anyone flushing the
  1886. // queue out regularly.
  1887. newUpdateMask |= InputUpdateType.Editor;
  1888. #endif
  1889. updateMask = newUpdateMask;
  1890. ////TODO: optimize this so that we don't repeatedly recreate state if we add/remove multiple devices
  1891. //// (same goes for not resolving actions repeatedly)
  1892. // Check if there's any native device we aren't using ATM which now fits
  1893. // the set of supported devices.
  1894. AddAvailableDevicesThatAreNowRecognized();
  1895. // If the settings restrict the set of supported devices, demote any native
  1896. // device we currently have that doesn't fit the requirements.
  1897. if (settings.supportedDevices.Count > 0)
  1898. {
  1899. for (var i = 0; i < m_DevicesCount; ++i)
  1900. {
  1901. var device = m_Devices[i];
  1902. var layout = device.m_Layout;
  1903. // If it's not in m_AvailableDevices, we don't automatically remove it.
  1904. // Whatever has been added directly through AddDevice(), we keep and don't
  1905. // restrict by `supportDevices`.
  1906. var isInAvailableDevices = false;
  1907. for (var n = 0; n < m_AvailableDeviceCount; ++n)
  1908. {
  1909. if (m_AvailableDevices[n].deviceId == device.deviceId)
  1910. {
  1911. isInAvailableDevices = true;
  1912. break;
  1913. }
  1914. }
  1915. if (!isInAvailableDevices)
  1916. continue;
  1917. // If the device layout isn't supported according to the current settings,
  1918. // remove the device.
  1919. if (!IsDeviceLayoutMarkedAsSupportedInSettings(layout))
  1920. {
  1921. RemoveDevice(device, keepOnListOfAvailableDevices: true);
  1922. --i;
  1923. }
  1924. }
  1925. }
  1926. // Cache some values.
  1927. Touchscreen.s_TapTime = settings.defaultTapTime;
  1928. Touchscreen.s_TapDelayTime = settings.multiTapDelayTime;
  1929. Touchscreen.s_TapRadiusSquared = settings.tapRadius * settings.tapRadius;
  1930. ButtonControl.s_GlobalDefaultButtonPressPoint = settings.defaultButtonPressPoint;
  1931. // Let listeners know.
  1932. for (var i = 0; i < m_SettingsChangedListeners.length; ++i)
  1933. m_SettingsChangedListeners[i]();
  1934. }
  1935. internal void AddAvailableDevicesThatAreNowRecognized()
  1936. {
  1937. for (var i = 0; i < m_AvailableDeviceCount; ++i)
  1938. {
  1939. var id = m_AvailableDevices[i].deviceId;
  1940. if (TryGetDeviceById(id) != null)
  1941. continue;
  1942. var layout = TryFindMatchingControlLayout(ref m_AvailableDevices[i].description, id);
  1943. if (IsDeviceLayoutMarkedAsSupportedInSettings(layout))
  1944. {
  1945. try
  1946. {
  1947. AddDevice(m_AvailableDevices[i].description, false,
  1948. deviceId: id,
  1949. deviceFlags: m_AvailableDevices[i].isNative ? InputDevice.DeviceFlags.Native : 0);
  1950. }
  1951. catch (Exception)
  1952. {
  1953. }
  1954. }
  1955. }
  1956. }
  1957. private unsafe void OnFocusChanged(bool focus)
  1958. {
  1959. ////REVIEW: should we also flush the event queue on focus loss?
  1960. // On focus loss, reset devices.
  1961. if (!focus)
  1962. {
  1963. // When running in background is enabled for the application, we only reset devices that aren't
  1964. // marked as canRunInBackground.
  1965. var runInBackground = m_Runtime.runInBackground;
  1966. // Find the size of the largest state block. This determines the amount of temporary memory we
  1967. // need to allocate.
  1968. var largestDeviceStateBlock = 0;
  1969. var deviceCount = m_DevicesCount;
  1970. for (var i = 0; i < deviceCount; ++i)
  1971. largestDeviceStateBlock = Math.Max(largestDeviceStateBlock, (int)m_Devices[i].m_StateBlock.alignedSizeInBytes);
  1972. // Allocate temp memory to hold one state event.
  1973. ////REVIEW: the need for an event here is sufficiently obscure to warrant scrutiny; likely, there's a better way
  1974. //// to tell synthetic input (or input sources in general) apart
  1975. // NOTE: We wrap the reset in an artificial state event so that it appears to the rest of the system
  1976. // like any other input. If we don't do that but rather just call UpdateState() with a null event
  1977. // pointer, the change will be considered an internal state change and will get ignored by some
  1978. // pieces of code (such as EnhancedTouch which filters out internal state changes of Touchscreen
  1979. // by ignoring any change that is not coming from an input event).
  1980. using (var tempBuffer =
  1981. new NativeArray<byte>(InputEvent.kBaseEventSize + sizeof(int) + largestDeviceStateBlock, Allocator.Temp))
  1982. {
  1983. var stateEventPtr = (StateEvent*)tempBuffer.GetUnsafePtr();
  1984. var statePtr = stateEventPtr->state;
  1985. var currentTime = m_Runtime.currentTime;
  1986. var updateType = defaultUpdateType;
  1987. for (var i = 0; i < deviceCount; ++i)
  1988. {
  1989. var device = m_Devices[i];
  1990. // Skip disabled devices.
  1991. if (!device.enabled)
  1992. continue;
  1993. // If the app will keep running in the background and the device is marked as being
  1994. // able to run in the background, don't touch it.
  1995. if (runInBackground && device.canRunInBackground)
  1996. continue;
  1997. // Set up the state event.
  1998. ref var stateBlock = ref device.m_StateBlock;
  1999. var deviceStateBlockSize = stateBlock.alignedSizeInBytes;
  2000. stateEventPtr->baseEvent.type = StateEvent.Type;
  2001. stateEventPtr->baseEvent.sizeInBytes = InputEvent.kBaseEventSize + sizeof(int) + deviceStateBlockSize;
  2002. stateEventPtr->baseEvent.time = currentTime;
  2003. stateEventPtr->baseEvent.deviceId = device.deviceId;
  2004. stateEventPtr->baseEvent.eventId = -1;
  2005. stateEventPtr->stateFormat = device.m_StateBlock.format;
  2006. // Set up new state.
  2007. var defaultStatePtr = device.defaultStatePtr;
  2008. if (device.noisy)
  2009. {
  2010. // The device has noisy controls. We don't want to reset those as they mostly
  2011. // represent sensor input and resetting sensor samples to default values isn't a good
  2012. // a good idea.
  2013. //
  2014. // Copy everything from defaultStatePtr except for the bits that are flagged in the
  2015. // device's noise mask.
  2016. var currentStatePtr = device.currentStatePtr;
  2017. var noiseMaskPtr = device.noiseMaskPtr;
  2018. // To preserve values from noisy controls, we need to first copy their current values.
  2019. UnsafeUtility.MemCpy(statePtr,
  2020. (byte*)currentStatePtr + stateBlock.byteOffset,
  2021. deviceStateBlockSize);
  2022. // And then we copy over default values masked by noise bits.
  2023. MemoryHelpers.MemCpyMasked(statePtr,
  2024. (byte*)defaultStatePtr + stateBlock.byteOffset,
  2025. (int)deviceStateBlockSize,
  2026. (byte*)noiseMaskPtr + stateBlock.byteOffset);
  2027. }
  2028. else
  2029. {
  2030. // No noisy controls in device. Just take the default state and put it in the event
  2031. // as is.
  2032. UnsafeUtility.MemCpy(statePtr,
  2033. (byte*)defaultStatePtr + stateBlock.byteOffset,
  2034. deviceStateBlockSize);
  2035. }
  2036. // Perform the reset.
  2037. UpdateState(device, updateType, statePtr, 0, deviceStateBlockSize, currentTime,
  2038. new InputEventPtr((InputEvent*)stateEventPtr));
  2039. // Tell the backend to reset.
  2040. device.RequestReset();
  2041. }
  2042. }
  2043. }
  2044. // We set this *after* the block above as defaultUpdateType is influenced by the setting.
  2045. m_HasFocus = focus;
  2046. }
  2047. private bool ShouldRunUpdate(InputUpdateType updateType)
  2048. {
  2049. // We perform a "null" update after domain reloads and on startup to get our devices
  2050. // in place before the runtime calls MonoBehaviour callbacks. See InputSystem.RunInitialUpdate().
  2051. if (updateType == InputUpdateType.None)
  2052. return true;
  2053. var mask = m_UpdateMask;
  2054. #if UNITY_EDITOR
  2055. // Ignore editor updates when the game is playing and has focus. All input goes to player.
  2056. if (gameIsPlayingAndHasFocus)
  2057. mask &= ~InputUpdateType.Editor;
  2058. // If the player isn't running, the only thing we run is editor updates.
  2059. else if (updateType != InputUpdateType.Editor)
  2060. return false;
  2061. #endif
  2062. return (updateType & mask) != 0;
  2063. }
  2064. /// <summary>
  2065. /// Process input events.
  2066. /// </summary>
  2067. /// <param name="updateType"></param>
  2068. /// <param name="eventBuffer"></param>
  2069. /// <remarks>
  2070. /// This method is the core workhorse of the input system. It is called from <see cref="UnityEngineInternal.Input.NativeInputSystem"/>.
  2071. /// Usually this happens in response to the player loop running and triggering updates at set points. However,
  2072. /// updates can also be manually triggered through <see cref="InputSystem.Update"/>.
  2073. ///
  2074. /// The method receives the event buffer used internally by the runtime to collect events.
  2075. ///
  2076. /// Note that update types do *NOT* say what the events we receive are for. The update type only indicates
  2077. /// where in the Unity's application loop we got called from. Where the event data goes depends wholly on
  2078. /// which buffers we activate in the update and write the event data into.
  2079. /// </remarks>
  2080. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1809:AvoidExcessiveLocals", Justification = "TODO: Refactor later.")]
  2081. private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer eventBuffer)
  2082. {
  2083. ////TODO: switch from Profiler to CustomSampler API
  2084. // NOTE: This is *not* using try/finally as we've seen unreliability in the EndSample()
  2085. // execution (and we're not sure where it's coming from).
  2086. Profiler.BeginSample("InputUpdate");
  2087. // Restore devices before checking update mask. See InputSystem.RunInitialUpdate().
  2088. RestoreDevicesAfterDomainReloadIfNecessary();
  2089. if ((updateType & m_UpdateMask) == 0)
  2090. {
  2091. Profiler.EndSample();
  2092. return;
  2093. }
  2094. WarnAboutDevicesFailingToRecreateAfterDomainReload();
  2095. // First update sends out startup analytics.
  2096. #if UNITY_ANALYTICS || UNITY_EDITOR
  2097. if (!m_HaveSentStartupAnalytics)
  2098. {
  2099. InputAnalytics.OnStartup(this);
  2100. m_HaveSentStartupAnalytics = true;
  2101. }
  2102. #endif
  2103. ////TODO: manual mode must be treated like lockInputToGameView in editor
  2104. // Update metrics.
  2105. m_Metrics.totalEventCount += eventBuffer.eventCount - (int)InputUpdate.s_LastUpdateRetainedEventCount;
  2106. m_Metrics.totalEventBytes += (int)eventBuffer.sizeInBytes - (int)InputUpdate.s_LastUpdateRetainedEventBytes;
  2107. ++m_Metrics.totalUpdateCount;
  2108. InputUpdate.s_LastUpdateRetainedEventCount = 0;
  2109. InputUpdate.s_LastUpdateRetainedEventBytes = 0;
  2110. // Store current time offset.
  2111. InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup = m_Runtime.currentTimeOffsetToRealtimeSinceStartup;
  2112. InputUpdate.s_LastUpdateType = updateType;
  2113. InputStateBuffers.SwitchTo(m_StateBuffers, updateType);
  2114. var isBeforeRenderUpdate = false;
  2115. if (updateType == InputUpdateType.Dynamic || updateType == InputUpdateType.Manual || updateType == InputUpdateType.Fixed)
  2116. {
  2117. ++InputUpdate.s_UpdateStepCount;
  2118. }
  2119. else if (updateType == InputUpdateType.BeforeRender)
  2120. {
  2121. isBeforeRenderUpdate = true;
  2122. }
  2123. // See if we're supposed to only take events up to a certain time.
  2124. // NOTE: We do not require the events in the queue to be sorted. Instead, we will walk over
  2125. // all events in the buffer each time. Note that if there are multiple events for the same
  2126. // device, it depends on the producer of these events to queue them in correct order.
  2127. // Otherwise, once an event with a newer timestamp has been processed, events coming later
  2128. // in the buffer and having older timestamps will get rejected.
  2129. var currentTime = updateType == InputUpdateType.Fixed ? m_Runtime.currentTimeForFixedUpdate : m_Runtime.currentTime;
  2130. var timesliceEvents = gameIsPlayingAndHasFocus && InputSystem.settings.updateMode == InputSettings.UpdateMode.ProcessEventsInFixedUpdate;
  2131. // Early out if there's no events to process.
  2132. if (eventBuffer.eventCount <= 0)
  2133. {
  2134. // Normally, we process action timeouts after first processing all events. If we have no
  2135. // events, we still need to check timeouts.
  2136. if (gameIsPlayingAndHasFocus)
  2137. ProcessStateChangeMonitorTimeouts();
  2138. #if ENABLE_PROFILER
  2139. Profiler.EndSample();
  2140. #endif
  2141. InvokeAfterUpdateCallback();
  2142. eventBuffer.Reset();
  2143. return;
  2144. }
  2145. var currentEventReadPtr =
  2146. (InputEvent*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(eventBuffer.data);
  2147. var remainingEventCount = eventBuffer.eventCount;
  2148. var processingStartTime = Time.realtimeSinceStartup;
  2149. // When timeslicing events or in before-render updates, we may be leaving events in the buffer
  2150. // for later processing. We do this by compacting the event buffer and moving events down such
  2151. // that the events we leave in the buffer form one contiguous chunk of memory at the beginning
  2152. // of the buffer.
  2153. var currentEventWritePtr = currentEventReadPtr;
  2154. var numEventsRetainedInBuffer = 0;
  2155. var totalEventLag = 0.0;
  2156. // Handle events.
  2157. while (remainingEventCount > 0)
  2158. {
  2159. InputDevice device = null;
  2160. Debug.Assert(!currentEventReadPtr->handled);
  2161. // In before render updates, we only take state events and only those for devices
  2162. // that have before render updates enabled.
  2163. if (isBeforeRenderUpdate)
  2164. {
  2165. while (remainingEventCount > 0)
  2166. {
  2167. Debug.Assert(!currentEventReadPtr->handled);
  2168. device = TryGetDeviceById(currentEventReadPtr->deviceId);
  2169. if (device != null && device.updateBeforeRender &&
  2170. (currentEventReadPtr->type == StateEvent.Type ||
  2171. currentEventReadPtr->type == DeltaStateEvent.Type))
  2172. break;
  2173. eventBuffer.AdvanceToNextEvent(ref currentEventReadPtr, ref currentEventWritePtr,
  2174. ref numEventsRetainedInBuffer, ref remainingEventCount, leaveEventInBuffer: true);
  2175. }
  2176. }
  2177. if (remainingEventCount == 0)
  2178. break;
  2179. var currentEventTimeInternal = currentEventReadPtr->internalTime;
  2180. // In the editor, we discard all input events that occur in-between exiting edit mode and having
  2181. // entered play mode as otherwise we'll spill a bunch of UI events that have occurred while the
  2182. // UI was sort of neither in this mode nor in that mode. This would usually lead to the game receiving
  2183. // an accumulation of spurious inputs right in one of its first updates.
  2184. //
  2185. // NOTE: There's a chance the solution here will prove inadequate on the long run. We may do things
  2186. // here such as throwing partial touches away and then letting the rest of a touch go through.
  2187. // Could be that ultimately we need to issue a full reset of all devices at the beginning of
  2188. // play mode in the editor.
  2189. #if UNITY_EDITOR
  2190. if ((updateType & InputUpdateType.Editor) == 0 &&
  2191. InputSystem.s_SystemObject.exitEditModeTime > 0 &&
  2192. currentEventTimeInternal >= InputSystem.s_SystemObject.exitEditModeTime &&
  2193. (currentEventTimeInternal < InputSystem.s_SystemObject.enterPlayModeTime ||
  2194. InputSystem.s_SystemObject.enterPlayModeTime == 0))
  2195. {
  2196. eventBuffer.AdvanceToNextEvent(ref currentEventReadPtr, ref currentEventWritePtr,
  2197. ref numEventsRetainedInBuffer, ref remainingEventCount, leaveEventInBuffer: false);
  2198. continue;
  2199. }
  2200. #endif
  2201. // If we're timeslicing, check if the event time is within limits.
  2202. if (timesliceEvents && currentEventTimeInternal >= currentTime)
  2203. {
  2204. eventBuffer.AdvanceToNextEvent(ref currentEventReadPtr, ref currentEventWritePtr,
  2205. ref numEventsRetainedInBuffer, ref remainingEventCount, leaveEventInBuffer: true);
  2206. continue;
  2207. }
  2208. if (currentEventTimeInternal <= currentTime)
  2209. totalEventLag += currentTime - currentEventTimeInternal;
  2210. // Grab device for event. In before-render updates, we already had to
  2211. // check the device.
  2212. if (device == null)
  2213. device = TryGetDeviceById(currentEventReadPtr->deviceId);
  2214. if (device == null)
  2215. {
  2216. #if UNITY_EDITOR
  2217. ////TODO: see if this is a device we haven't created and if so, just ignore
  2218. m_Diagnostics?.OnCannotFindDeviceForEvent(new InputEventPtr(currentEventReadPtr));
  2219. #endif
  2220. eventBuffer.AdvanceToNextEvent(ref currentEventReadPtr, ref currentEventWritePtr,
  2221. ref numEventsRetainedInBuffer, ref remainingEventCount, leaveEventInBuffer: false);
  2222. // No device found matching event. Ignore it.
  2223. continue;
  2224. }
  2225. // Give listeners a shot at the event.
  2226. if (m_EventListeners.length > 0)
  2227. {
  2228. for (var i = 0; i < m_EventListeners.length; ++i)
  2229. m_EventListeners[i](new InputEventPtr(currentEventReadPtr), device);
  2230. // If a listener marks the event as handled, we don't process it further.
  2231. if (currentEventReadPtr->handled)
  2232. {
  2233. eventBuffer.AdvanceToNextEvent(ref currentEventReadPtr, ref currentEventWritePtr,
  2234. ref numEventsRetainedInBuffer, ref remainingEventCount, leaveEventInBuffer: false);
  2235. continue;
  2236. }
  2237. }
  2238. // Process.
  2239. var currentEventType = currentEventReadPtr->type;
  2240. switch (currentEventType)
  2241. {
  2242. case StateEvent.Type:
  2243. case DeltaStateEvent.Type:
  2244. var eventPtr = new InputEventPtr(currentEventReadPtr);
  2245. // Ignore state changes if device is disabled.
  2246. if (!device.enabled)
  2247. {
  2248. #if UNITY_EDITOR
  2249. m_Diagnostics?.OnEventForDisabledDevice(eventPtr, device);
  2250. #endif
  2251. break;
  2252. }
  2253. var deviceIsStateCallbackReceiver = (device.m_DeviceFlags & InputDevice.DeviceFlags.HasStateCallbacks) ==
  2254. InputDevice.DeviceFlags.HasStateCallbacks;
  2255. // Ignore the event if the last state update we received for the device was
  2256. // newer than this state event is. We don't allow devices to go back in time.
  2257. //
  2258. // NOTE: We make an exception here for devices that implement IInputStateCallbackReceiver (such
  2259. // as Touchscreen). For devices that dynamically incorporate state it can be hard ensuring
  2260. // a global ordering of events as there may be multiple substreams (e.g. each individual touch)
  2261. // that are generated in the backend and would require considerable work to ensure monotonically
  2262. // increasing timestamps across all such streams.
  2263. if (currentEventTimeInternal < device.m_LastUpdateTimeInternal &&
  2264. !(deviceIsStateCallbackReceiver && device.stateBlock.format != eventPtr.stateFormat))
  2265. {
  2266. #if UNITY_EDITOR
  2267. m_Diagnostics?.OnEventTimestampOutdated(new InputEventPtr(currentEventReadPtr), device);
  2268. #endif
  2269. break;
  2270. }
  2271. // Update the state of the device from the event. If the device is an IInputStateCallbackReceiver,
  2272. // let the device handle the event. If not, we do it ourselves.
  2273. var haveChangedStateOtherThanNoise = true;
  2274. if (deviceIsStateCallbackReceiver)
  2275. {
  2276. // NOTE: We leave it to the device to make sure the event has the right format. This allows the
  2277. // device to handle multiple different incoming formats.
  2278. ((IInputStateCallbackReceiver)device).OnStateEvent(eventPtr);
  2279. }
  2280. else
  2281. {
  2282. // If the state format doesn't match, ignore the event.
  2283. if (device.stateBlock.format != eventPtr.stateFormat)
  2284. {
  2285. #if UNITY_EDITOR
  2286. m_Diagnostics?.OnEventFormatMismatch(currentEventReadPtr, device);
  2287. #endif
  2288. break;
  2289. }
  2290. haveChangedStateOtherThanNoise = UpdateState(device, eventPtr, updateType);
  2291. }
  2292. // Update timestamp on device.
  2293. // NOTE: We do this here and not in UpdateState() so that InputState.Change() will *NOT* change timestamps.
  2294. // Only events should.
  2295. if (device.m_LastUpdateTimeInternal <= eventPtr.internalTime)
  2296. device.m_LastUpdateTimeInternal = eventPtr.internalTime;
  2297. // Make device current. Again, only do this when receiving events.
  2298. if (haveChangedStateOtherThanNoise)
  2299. device.MakeCurrent();
  2300. break;
  2301. case TextEvent.Type:
  2302. {
  2303. var textEventPtr = (TextEvent*)currentEventReadPtr;
  2304. if (device is ITextInputReceiver textInputReceiver)
  2305. {
  2306. var utf32Char = textEventPtr->character;
  2307. if (utf32Char >= 0x10000)
  2308. {
  2309. // Send surrogate pair.
  2310. utf32Char -= 0x10000;
  2311. var highSurrogate = 0xD800 + ((utf32Char >> 10) & 0x3FF);
  2312. var lowSurrogate = 0xDC00 + (utf32Char & 0x3FF);
  2313. textInputReceiver.OnTextInput((char)highSurrogate);
  2314. textInputReceiver.OnTextInput((char)lowSurrogate);
  2315. }
  2316. else
  2317. {
  2318. // Send single, plain character.
  2319. textInputReceiver.OnTextInput((char)utf32Char);
  2320. }
  2321. }
  2322. break;
  2323. }
  2324. case IMECompositionEvent.Type:
  2325. {
  2326. var imeEventPtr = (IMECompositionEvent*)currentEventReadPtr;
  2327. var textInputReceiver = device as ITextInputReceiver;
  2328. textInputReceiver?.OnIMECompositionChanged(imeEventPtr->compositionString);
  2329. break;
  2330. }
  2331. case DeviceRemoveEvent.Type:
  2332. {
  2333. RemoveDevice(device, keepOnListOfAvailableDevices: false);
  2334. // If it's a native device with a description, put it on the list of disconnected
  2335. // devices.
  2336. if (device.native && !device.description.empty)
  2337. {
  2338. ArrayHelpers.AppendWithCapacity(ref m_DisconnectedDevices, ref m_DisconnectedDevicesCount, device);
  2339. for (var i = 0; i < m_DeviceChangeListeners.length; ++i)
  2340. m_DeviceChangeListeners[i](device, InputDeviceChange.Disconnected);
  2341. }
  2342. break;
  2343. }
  2344. case DeviceConfigurationEvent.Type:
  2345. device.OnConfigurationChanged();
  2346. InputActionState.OnDeviceChange(device, InputDeviceChange.ConfigurationChanged);
  2347. for (var i = 0; i < m_DeviceChangeListeners.length; ++i)
  2348. m_DeviceChangeListeners[i](device, InputDeviceChange.ConfigurationChanged);
  2349. break;
  2350. }
  2351. eventBuffer.AdvanceToNextEvent(ref currentEventReadPtr, ref currentEventWritePtr,
  2352. ref numEventsRetainedInBuffer, ref remainingEventCount, leaveEventInBuffer: false);
  2353. }
  2354. m_Metrics.totalEventProcessingTime += Time.realtimeSinceStartup - processingStartTime;
  2355. m_Metrics.totalEventLagTime += totalEventLag;
  2356. // Remember how much data we retained so that we don't count it against the next
  2357. // batch of events that we receive.
  2358. InputUpdate.s_LastUpdateRetainedEventCount = (uint)numEventsRetainedInBuffer;
  2359. InputUpdate.s_LastUpdateRetainedEventBytes = (uint)((byte*)currentEventWritePtr -
  2360. (byte*)NativeArrayUnsafeUtility
  2361. .GetUnsafeBufferPointerWithoutChecks(eventBuffer
  2362. .data));
  2363. // Update event buffer. If we have retained events, update event count
  2364. // and buffer size. If not, just reset.
  2365. if (numEventsRetainedInBuffer > 0)
  2366. {
  2367. var bufferPtr = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(eventBuffer.data);
  2368. Debug.Assert((byte*)currentEventWritePtr > (byte*)bufferPtr);
  2369. var newBufferSize = (byte*)currentEventWritePtr - (byte*)bufferPtr;
  2370. eventBuffer = new InputEventBuffer((InputEvent*)bufferPtr, numEventsRetainedInBuffer, (int)newBufferSize,
  2371. (int)eventBuffer.capacityInBytes);
  2372. }
  2373. else
  2374. {
  2375. eventBuffer.Reset();
  2376. }
  2377. if (gameIsPlayingAndHasFocus)
  2378. ProcessStateChangeMonitorTimeouts();
  2379. ////TODO: fire event that allows code to update state *from* state we just updated
  2380. Profiler.EndSample();
  2381. ////FIXME: need to ensure that if someone calls QueueEvent() from an onAfterUpdate callback, we don't end up with a
  2382. //// mess in the event buffer
  2383. //// same goes for events that someone may queue from a change monitor callback
  2384. InvokeAfterUpdateCallback();
  2385. ////TODO: check if there's new events in the event buffer; if so, do a pass over those events right away
  2386. }
  2387. private void InvokeAfterUpdateCallback()
  2388. {
  2389. for (var i = 0; i < m_AfterUpdateListeners.length; ++i)
  2390. m_AfterUpdateListeners[i]();
  2391. }
  2392. // NOTE: 'newState' can be a subset of the full state stored at 'oldState'. In this case,
  2393. // 'newStateOffsetInBytes' must give the offset into the full state and 'newStateSizeInBytes' must
  2394. // give the size of memory slice to be updated.
  2395. private unsafe bool ProcessStateChangeMonitors(int deviceIndex, void* newStateFromEvent, void* oldStateOfDevice, uint newStateSizeInBytes, uint newStateOffsetInBytes)
  2396. {
  2397. if (m_StateChangeMonitors == null)
  2398. return false;
  2399. // We resize the monitor arrays only when someone adds to them so they
  2400. // may be out of sync with the size of m_Devices.
  2401. if (deviceIndex >= m_StateChangeMonitors.Length)
  2402. return false;
  2403. var memoryRegions = m_StateChangeMonitors[deviceIndex].memoryRegions;
  2404. if (memoryRegions == null)
  2405. return false; // No one cares about state changes on this device.
  2406. var numMonitors = m_StateChangeMonitors[deviceIndex].count;
  2407. var signalled = false;
  2408. var signals = m_StateChangeMonitors[deviceIndex].signalled;
  2409. var haveChangedSignalsBitfield = false;
  2410. // For every memory region that overlaps what we got in the event, compare memory contents
  2411. // between the old device state and what's in the event. If the contents different, the
  2412. // respective state monitor signals.
  2413. var newEventMemoryRegion = new MemoryHelpers.BitRegion(newStateOffsetInBytes, 0, newStateSizeInBytes * 8);
  2414. for (var i = 0; i < numMonitors; ++i)
  2415. {
  2416. var memoryRegion = memoryRegions[i];
  2417. // Check if the monitor record has been wiped in the meantime. If so, remove it.
  2418. if (memoryRegion.sizeInBits == 0)
  2419. {
  2420. ////REVIEW: Do we really care? It is nice that it's predictable this way but hardly a hard requirement
  2421. // NOTE: We're using EraseAtWithCapacity here rather than EraseAtByMovingTail to preserve
  2422. // order which makes the order of callbacks somewhat more predictable.
  2423. var listenerCount = numMonitors;
  2424. var memoryRegionCount = numMonitors;
  2425. ArrayHelpers.EraseAtWithCapacity(m_StateChangeMonitors[deviceIndex].listeners, ref listenerCount, i);
  2426. ArrayHelpers.EraseAtWithCapacity(memoryRegions, ref memoryRegionCount, i);
  2427. signals.SetLength(numMonitors - 1);
  2428. haveChangedSignalsBitfield = true;
  2429. --numMonitors;
  2430. --i;
  2431. continue;
  2432. }
  2433. var overlap = newEventMemoryRegion.Overlap(memoryRegion);
  2434. if (overlap.isEmpty || MemoryHelpers.Compare(oldStateOfDevice, (byte*)newStateFromEvent - newStateOffsetInBytes, overlap))
  2435. continue;
  2436. signals.SetBit(i);
  2437. haveChangedSignalsBitfield = true;
  2438. signalled = true;
  2439. }
  2440. if (haveChangedSignalsBitfield)
  2441. m_StateChangeMonitors[deviceIndex].signalled = signals;
  2442. return signalled;
  2443. }
  2444. private unsafe void FireStateChangeNotifications(int deviceIndex, double internalTime, InputEvent* eventPtr)
  2445. {
  2446. Debug.Assert(m_StateChangeMonitors != null);
  2447. Debug.Assert(m_StateChangeMonitors.Length > deviceIndex);
  2448. // NOTE: This method must be safe for mutating the state change monitor arrays from *within*
  2449. // NotifyControlStateChanged()! This includes all monitors for the device being wiped
  2450. // completely or arbitrary additions and removals having occurred.
  2451. ref var signals = ref m_StateChangeMonitors[deviceIndex].signalled;
  2452. ref var listeners = ref m_StateChangeMonitors[deviceIndex].listeners;
  2453. var time = internalTime - InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup;
  2454. // Call IStateChangeMonitor.NotifyControlStateChange for every monitor that is in
  2455. // signalled state.
  2456. for (var i = 0; i < signals.length; ++i)
  2457. {
  2458. if (!signals.TestBit(i))
  2459. continue;
  2460. var listener = listeners[i];
  2461. try
  2462. {
  2463. listener.monitor.NotifyControlStateChanged(listener.control, time, eventPtr,
  2464. listener.monitorIndex);
  2465. }
  2466. catch (Exception exception)
  2467. {
  2468. Debug.LogError(
  2469. $"Exception '{exception.GetType().Name}' thrown from state change monitor '{listener.monitor.GetType().Name}' on '{listener.control}'");
  2470. Debug.LogException(exception);
  2471. }
  2472. signals.ClearBit(i);
  2473. }
  2474. }
  2475. private void ProcessStateChangeMonitorTimeouts()
  2476. {
  2477. if (m_StateChangeMonitorTimeouts.length == 0)
  2478. return;
  2479. // Go through the list and both trigger expired timers and remove any irrelevant
  2480. // ones by compacting the array.
  2481. // NOTE: We do not actually release any memory we may have allocated.
  2482. var currentTime = m_Runtime.currentTime - InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup;
  2483. var remainingTimeoutCount = 0;
  2484. for (var i = 0; i < m_StateChangeMonitorTimeouts.length; ++i)
  2485. {
  2486. // If we have reset this entry in RemoveStateChangeMonitorTimeouts(),
  2487. // skip over it and let compaction get rid of it.
  2488. if (m_StateChangeMonitorTimeouts[i].control == null)
  2489. continue;
  2490. var timerExpirationTime = m_StateChangeMonitorTimeouts[i].time;
  2491. if (timerExpirationTime <= currentTime)
  2492. {
  2493. var timeout = m_StateChangeMonitorTimeouts[i];
  2494. timeout.monitor.NotifyTimerExpired(timeout.control,
  2495. currentTime, timeout.monitorIndex, timeout.timerIndex);
  2496. // Compaction will get rid of the entry.
  2497. }
  2498. else
  2499. {
  2500. // Rather than repeatedly calling RemoveAt() and thus potentially
  2501. // moving the same data over and over again, we compact the array
  2502. // on the fly and move entries in the array down as needed.
  2503. if (i != remainingTimeoutCount)
  2504. m_StateChangeMonitorTimeouts[remainingTimeoutCount] = m_StateChangeMonitorTimeouts[i];
  2505. ++remainingTimeoutCount;
  2506. }
  2507. }
  2508. m_StateChangeMonitorTimeouts.SetLength(remainingTimeoutCount);
  2509. }
  2510. internal unsafe bool UpdateState(InputDevice device, InputEvent* eventPtr, InputUpdateType updateType)
  2511. {
  2512. Debug.Assert(eventPtr != null, "Received NULL event ptr");
  2513. var stateBlockOfDevice = device.m_StateBlock;
  2514. var stateBlockSizeOfDevice = stateBlockOfDevice.sizeInBits / 8; // Always byte-aligned; avoid calling alignedSizeInBytes.
  2515. var offsetInDeviceStateToCopyTo = 0u;
  2516. uint sizeOfStateToCopy;
  2517. uint receivedStateSize;
  2518. byte* ptrToReceivedState;
  2519. FourCC receivedStateFormat;
  2520. // Grab state data from event and decide where to copy to and how much to copy.
  2521. if (eventPtr->type == StateEvent.Type)
  2522. {
  2523. var stateEventPtr = (StateEvent*)eventPtr;
  2524. receivedStateFormat = stateEventPtr->stateFormat;
  2525. receivedStateSize = stateEventPtr->stateSizeInBytes;
  2526. ptrToReceivedState = (byte*)stateEventPtr->state;
  2527. // Ignore extra state at end of event.
  2528. sizeOfStateToCopy = receivedStateSize;
  2529. if (sizeOfStateToCopy > stateBlockSizeOfDevice)
  2530. sizeOfStateToCopy = stateBlockSizeOfDevice;
  2531. }
  2532. else
  2533. {
  2534. Debug.Assert(eventPtr->type == DeltaStateEvent.Type, "Given event must either be a StateEvent or a DeltaStateEvent");
  2535. var deltaEventPtr = (DeltaStateEvent*)eventPtr;
  2536. receivedStateFormat = deltaEventPtr->stateFormat;
  2537. receivedStateSize = deltaEventPtr->deltaStateSizeInBytes;
  2538. ptrToReceivedState = (byte*)deltaEventPtr->deltaState;
  2539. offsetInDeviceStateToCopyTo = deltaEventPtr->stateOffset;
  2540. // Ignore extra state at end of event.
  2541. sizeOfStateToCopy = receivedStateSize;
  2542. if (offsetInDeviceStateToCopyTo + sizeOfStateToCopy > stateBlockSizeOfDevice)
  2543. {
  2544. if (offsetInDeviceStateToCopyTo >= stateBlockSizeOfDevice)
  2545. return false; // Entire delta state is out of range.
  2546. sizeOfStateToCopy = stateBlockSizeOfDevice - offsetInDeviceStateToCopyTo;
  2547. }
  2548. }
  2549. Debug.Assert(device.m_StateBlock.format == receivedStateFormat, "Received state format does not match format of device");
  2550. // Write state.
  2551. return UpdateState(device, updateType, ptrToReceivedState, offsetInDeviceStateToCopyTo,
  2552. sizeOfStateToCopy, eventPtr->internalTime, eventPtr);
  2553. }
  2554. /// <summary>
  2555. /// This method is the workhorse for updating input state in the system. It runs all the logic of incorporating
  2556. /// new state into devices and triggering whatever change monitors are attached to the state memory that gets
  2557. /// touched.
  2558. /// </summary>
  2559. /// <remarks>
  2560. /// This method can be invoked from outside the event processing loop and the given data does not have to come
  2561. /// from an event.
  2562. ///
  2563. /// This method does NOT respect <see cref="IInputStateCallbackReceiver"/>. This means that the device will
  2564. /// NOT get a shot at intervening in the state write.
  2565. /// </remarks>
  2566. /// <param name="device">Device to update state on. <paramref name="stateOffsetInDevice"/> is relative to device's
  2567. /// starting offset in memory.</param>
  2568. /// <param name="eventPtr">Pointer to state event from which the state change was initiated. Null if the state
  2569. /// change is not coming from an event.</param>
  2570. internal unsafe bool UpdateState(InputDevice device, InputUpdateType updateType,
  2571. void* statePtr, uint stateOffsetInDevice, uint stateSize, double internalTime, InputEventPtr eventPtr = default)
  2572. {
  2573. var deviceIndex = device.m_DeviceIndex;
  2574. ref var stateBlockOfDevice = ref device.m_StateBlock;
  2575. ////TODO: limit stateSize and StateOffset by the device's state memory
  2576. var deviceBuffer = (byte*)InputStateBuffers.GetFrontBufferForDevice(deviceIndex);
  2577. // Before we update state, let change monitors compare the old and the new state.
  2578. // We do this instead of first updating the front buffer and then comparing to the
  2579. // back buffer as that would require a buffer flip for each state change in order
  2580. // for the monitors to work reliably. By comparing the *event* data to the current
  2581. // state, we can have multiple state events in the same frame yet still get reliable
  2582. // change notifications.
  2583. var haveSignalledMonitors =
  2584. ProcessStateChangeMonitors(deviceIndex, statePtr,
  2585. deviceBuffer + stateBlockOfDevice.byteOffset,
  2586. stateSize, stateOffsetInDevice);
  2587. var deviceStateOffset = device.m_StateBlock.byteOffset + stateOffsetInDevice;
  2588. var deviceStatePtr = deviceBuffer + deviceStateOffset;
  2589. ////REVIEW: Should we do this only for events but not for InputState.Change()?
  2590. // If noise filtering on .current is turned on and the device may have noise,
  2591. // determine if the event carries signal or not.
  2592. var makeDeviceCurrent = true;
  2593. if (device.noisy && m_Settings.filterNoiseOnCurrent)
  2594. {
  2595. // Compare the current state of the device to the newly received state but overlay
  2596. // the comparison by the noise mask.
  2597. var noiseMask = (byte*)InputStateBuffers.s_NoiseMaskBuffer + deviceStateOffset;
  2598. makeDeviceCurrent =
  2599. !MemoryHelpers.MemCmpBitRegion(deviceStatePtr, statePtr,
  2600. 0, stateSize * 8, mask: noiseMask);
  2601. }
  2602. // Buffer flip.
  2603. var flipped = FlipBuffersForDeviceIfNecessary(device, updateType);
  2604. // Now write the state.
  2605. #if UNITY_EDITOR
  2606. if (updateType == InputUpdateType.Editor)
  2607. {
  2608. WriteStateChange(m_StateBuffers.m_EditorStateBuffers, deviceIndex, ref stateBlockOfDevice, stateOffsetInDevice,
  2609. statePtr, stateSize, flipped);
  2610. }
  2611. else
  2612. #endif
  2613. {
  2614. WriteStateChange(m_StateBuffers.m_PlayerStateBuffers, deviceIndex, ref stateBlockOfDevice,
  2615. stateOffsetInDevice, statePtr, stateSize, flipped);
  2616. }
  2617. // Notify listeners.
  2618. for (var i = 0; i < m_DeviceStateChangeListeners.length; ++i)
  2619. m_DeviceStateChangeListeners[i](device, eventPtr);
  2620. // Now that we've committed the new state to memory, if any of the change
  2621. // monitors fired, let the associated actions know.
  2622. if (haveSignalledMonitors)
  2623. FireStateChangeNotifications(deviceIndex, internalTime, eventPtr);
  2624. return makeDeviceCurrent;
  2625. }
  2626. private static unsafe void WriteStateChange(InputStateBuffers.DoubleBuffers buffers, int deviceIndex,
  2627. ref InputStateBlock deviceStateBlock, uint stateOffsetInDevice, void* statePtr, uint stateSizeInBytes, bool flippedBuffers)
  2628. {
  2629. var frontBuffer = buffers.GetFrontBuffer(deviceIndex);
  2630. Debug.Assert(frontBuffer != null);
  2631. // If we're updating less than the full state, we need to preserve the parts we are not updating.
  2632. // Instead of trying to optimize here and only copy what we really need, we just go and copy the
  2633. // entire state of the device over.
  2634. //
  2635. // NOTE: This copying must only happen once, right after a buffer flip. Otherwise we may copy old,
  2636. // stale input state from the back buffer over state that has already been updated with newer
  2637. // data.
  2638. var deviceStateSize = deviceStateBlock.sizeInBits / 8; // Always byte-aligned; avoid calling alignedSizeInBytes.
  2639. if (flippedBuffers && deviceStateSize != stateSizeInBytes)
  2640. {
  2641. var backBuffer = buffers.GetBackBuffer(deviceIndex);
  2642. Debug.Assert(backBuffer != null);
  2643. UnsafeUtility.MemCpy(
  2644. (byte*)frontBuffer + deviceStateBlock.byteOffset,
  2645. (byte*)backBuffer + deviceStateBlock.byteOffset,
  2646. deviceStateSize);
  2647. }
  2648. UnsafeUtility.MemCpy((byte*)frontBuffer + deviceStateBlock.byteOffset + stateOffsetInDevice, statePtr,
  2649. stateSizeInBytes);
  2650. }
  2651. // Flip front and back buffer for device, if necessary. May flip buffers for more than just
  2652. // the given update type.
  2653. // Returns true if there was a buffer flip.
  2654. private bool FlipBuffersForDeviceIfNecessary(InputDevice device, InputUpdateType updateType)
  2655. {
  2656. if (updateType == InputUpdateType.BeforeRender)
  2657. {
  2658. ////REVIEW: I think this is wrong; if we haven't flipped in the current dynamic or fixed update, we should do so now
  2659. // We never flip buffers for before render. Instead, we already write
  2660. // into the front buffer.
  2661. return false;
  2662. }
  2663. #if UNITY_EDITOR
  2664. ////REVIEW: should this use the editor update ticks as quasi-frame-boundaries?
  2665. // Updates go to the editor only if the game isn't playing or does not have focus.
  2666. // Otherwise we fall through to the logic that flips for the *next* dynamic and
  2667. // fixed updates.
  2668. if (updateType == InputUpdateType.Editor && !gameIsPlayingAndHasFocus)
  2669. {
  2670. // The editor doesn't really have a concept of frame-to-frame operation the
  2671. // same way the player does. So we simply flip buffers on a device whenever
  2672. // a new state event for it comes in.
  2673. m_StateBuffers.m_EditorStateBuffers.SwapBuffers(device.m_DeviceIndex);
  2674. return true;
  2675. }
  2676. #endif
  2677. // Flip buffers if we haven't already for this frame.
  2678. if (device.m_CurrentUpdateStepCount != InputUpdate.s_UpdateStepCount)
  2679. {
  2680. m_StateBuffers.m_PlayerStateBuffers.SwapBuffers(device.m_DeviceIndex);
  2681. device.m_CurrentUpdateStepCount = InputUpdate.s_UpdateStepCount;
  2682. return true;
  2683. }
  2684. return false;
  2685. }
  2686. // Domain reload survival logic. Also used for pushing and popping input system
  2687. // state for testing.
  2688. // Stuff everything that we want to survive a domain reload into
  2689. // a m_SerializedState.
  2690. #if UNITY_EDITOR || DEVELOPMENT_BUILD
  2691. [Serializable]
  2692. internal struct DeviceState
  2693. {
  2694. // Preserving InputDevices is somewhat tricky business. Serializing
  2695. // them in full would involve pretty nasty work. We have the restriction,
  2696. // however, that everything needs to be created from layouts (it partly
  2697. // exists for the sake of reload survivability), so we should be able to
  2698. // just go and recreate the device from the layout. This also has the
  2699. // advantage that if the layout changes between reloads, the change
  2700. // automatically takes effect.
  2701. public string name;
  2702. public string layout;
  2703. public string variants;
  2704. public string[] usages;
  2705. public int deviceId;
  2706. public int participantId;
  2707. public InputDevice.DeviceFlags flags;
  2708. public InputDeviceDescription description;
  2709. public void Restore(InputDevice device)
  2710. {
  2711. var usageCount = usages.LengthSafe();
  2712. for (var i = 0; i < usageCount; ++i)
  2713. device.AddDeviceUsage(new InternedString(usages[i]));
  2714. device.m_ParticipantId = participantId;
  2715. }
  2716. }
  2717. /// <summary>
  2718. /// State we take across domain reloads.
  2719. /// </summary>
  2720. /// <remarks>
  2721. /// Most of the state we re-recreate in-between reloads and do not store
  2722. /// in this structure. In particular, we do not preserve anything from
  2723. /// the various RegisterXXX().
  2724. /// </remarks>
  2725. [Serializable]
  2726. internal struct SerializedState
  2727. {
  2728. public int layoutRegistrationVersion;
  2729. public float pollingFrequency;
  2730. public DeviceState[] devices;
  2731. public AvailableDevice[] availableDevices;
  2732. public InputStateBuffers buffers;
  2733. public InputUpdate.SerializedState updateState;
  2734. public InputUpdateType updateMask;
  2735. public InputMetrics metrics;
  2736. public InputSettings settings;
  2737. #if UNITY_ANALYTICS || UNITY_EDITOR
  2738. public bool haveSentStartupAnalytics;
  2739. #endif
  2740. }
  2741. internal SerializedState SaveState()
  2742. {
  2743. // Devices.
  2744. var deviceCount = m_DevicesCount;
  2745. var deviceArray = new DeviceState[deviceCount];
  2746. for (var i = 0; i < deviceCount; ++i)
  2747. {
  2748. var device = m_Devices[i];
  2749. string[] usages = null;
  2750. if (device.usages.Count > 0)
  2751. usages = device.usages.Select(x => x.ToString()).ToArray();
  2752. var deviceState = new DeviceState
  2753. {
  2754. name = device.name,
  2755. layout = device.layout,
  2756. variants = device.variants,
  2757. deviceId = device.deviceId,
  2758. participantId = device.m_ParticipantId,
  2759. usages = usages,
  2760. description = device.m_Description,
  2761. flags = device.m_DeviceFlags
  2762. };
  2763. deviceArray[i] = deviceState;
  2764. }
  2765. return new SerializedState
  2766. {
  2767. layoutRegistrationVersion = m_LayoutRegistrationVersion,
  2768. pollingFrequency = m_PollingFrequency,
  2769. devices = deviceArray,
  2770. availableDevices = m_AvailableDevices?.Take(m_AvailableDeviceCount).ToArray(),
  2771. buffers = m_StateBuffers,
  2772. updateState = InputUpdate.Save(),
  2773. updateMask = m_UpdateMask,
  2774. metrics = m_Metrics,
  2775. settings = m_Settings,
  2776. #if UNITY_ANALYTICS || UNITY_EDITOR
  2777. haveSentStartupAnalytics = m_HaveSentStartupAnalytics,
  2778. #endif
  2779. };
  2780. }
  2781. internal void RestoreStateWithoutDevices(SerializedState state)
  2782. {
  2783. m_StateBuffers = state.buffers;
  2784. m_LayoutRegistrationVersion = state.layoutRegistrationVersion + 1;
  2785. updateMask = state.updateMask;
  2786. m_Metrics = state.metrics;
  2787. m_PollingFrequency = state.pollingFrequency;
  2788. if (m_Settings != null)
  2789. Object.DestroyImmediate(m_Settings);
  2790. m_Settings = state.settings;
  2791. #if UNITY_ANALYTICS || UNITY_EDITOR
  2792. m_HaveSentStartupAnalytics = state.haveSentStartupAnalytics;
  2793. #endif
  2794. ////REVIEW: instead of accessing globals here, we could move this to when we re-create devices
  2795. // Update state.
  2796. InputUpdate.Restore(state.updateState);
  2797. }
  2798. // If these are set, we clear them out on the first input update.
  2799. internal DeviceState[] m_SavedDeviceStates;
  2800. internal AvailableDevice[] m_SavedAvailableDevices;
  2801. /// <summary>
  2802. /// Recreate devices based on the devices we had before a domain reload.
  2803. /// </summary>
  2804. /// <remarks>
  2805. /// Note that device indices may change between domain reloads.
  2806. ///
  2807. /// We recreate devices using the layout information as it exists now as opposed to
  2808. /// as it existed before the domain reload. This means we'll be picking up any changes that
  2809. /// have happened to layouts as part of the reload (including layouts having been removed
  2810. /// entirely).
  2811. /// </remarks>
  2812. internal void RestoreDevicesAfterDomainReload()
  2813. {
  2814. Profiler.BeginSample("InputManager.RestoreDevicesAfterDomainReload");
  2815. using (InputDeviceBuilder.Ref())
  2816. {
  2817. DeviceState[] retainedDeviceStates = null;
  2818. var deviceStates = m_SavedDeviceStates;
  2819. var deviceCount = m_SavedDeviceStates.LengthSafe();
  2820. m_SavedDeviceStates = null; // Prevent layout matcher registering themselves on the fly from picking anything off this list.
  2821. for (var i = 0; i < deviceCount; ++i)
  2822. {
  2823. ref var deviceState = ref deviceStates[i];
  2824. var device = TryGetDeviceById(deviceState.deviceId);
  2825. if (device != null)
  2826. continue;
  2827. var layout = TryFindMatchingControlLayout(ref deviceState.description,
  2828. deviceState.deviceId);
  2829. if (layout.IsEmpty())
  2830. {
  2831. var previousLayout = new InternedString(deviceState.layout);
  2832. if (m_Layouts.HasLayout(previousLayout))
  2833. layout = previousLayout;
  2834. }
  2835. if (layout.IsEmpty() || !RestoreDeviceFromSavedState(ref deviceState, layout))
  2836. ArrayHelpers.Append(ref retainedDeviceStates, deviceState);
  2837. }
  2838. // See if we can make sense of an available device now that we couldn't make sense of
  2839. // before. This can be the case if there's new layout information that wasn't available
  2840. // before.
  2841. if (m_SavedAvailableDevices != null)
  2842. {
  2843. m_AvailableDevices = m_SavedAvailableDevices;
  2844. m_AvailableDeviceCount = m_SavedAvailableDevices.LengthSafe();
  2845. for (var i = 0; i < m_AvailableDeviceCount; ++i)
  2846. {
  2847. var device = TryGetDeviceById(m_AvailableDevices[i].deviceId);
  2848. if (device != null)
  2849. continue;
  2850. if (m_AvailableDevices[i].isRemoved)
  2851. continue;
  2852. var layout = TryFindMatchingControlLayout(ref m_AvailableDevices[i].description,
  2853. m_AvailableDevices[i].deviceId);
  2854. if (!layout.IsEmpty())
  2855. {
  2856. try
  2857. {
  2858. AddDevice(layout, m_AvailableDevices[i].deviceId,
  2859. deviceDescription: m_AvailableDevices[i].description,
  2860. deviceFlags: m_AvailableDevices[i].isNative ? InputDevice.DeviceFlags.Native : 0);
  2861. }
  2862. catch (Exception)
  2863. {
  2864. // Just ignore. Simply means we still can't really turn the device into something useful.
  2865. }
  2866. }
  2867. }
  2868. }
  2869. // Done. Discard saved arrays.
  2870. m_SavedDeviceStates = retainedDeviceStates;
  2871. m_SavedAvailableDevices = null;
  2872. }
  2873. Profiler.EndSample();
  2874. }
  2875. // We have two general types of devices we need to care about when recreating devices
  2876. // after domain reloads:
  2877. //
  2878. // A) device with InputDeviceDescription
  2879. // B) device created directly from specific layout
  2880. //
  2881. // A) should go through the normal matching process whereas B) should get recreated with
  2882. // layout of same name (if still available).
  2883. //
  2884. // So we kick device recreation off from two points:
  2885. //
  2886. // 1) From RegisterControlLayoutMatcher to catch A)
  2887. // 2) From RegisterControlLayout to catch B)
  2888. //
  2889. // Additionally, we have the complication that a layout a device was using was something
  2890. // dynamically registered from onFindLayoutForDevice. We don't do anything special about that.
  2891. // The first full input update will flush out the list of saved device states and at that
  2892. // point, any onFindLayoutForDevice hooks simply have to be in place. If they are, devices
  2893. // will get recreated appropriately.
  2894. //
  2895. // It would be much simpler to recreate all devices as the first thing in the first full input
  2896. // update but that would mean that devices would become available only very late. They would
  2897. // not, for example, be available when MonoBehaviour.Start methods are invoked.
  2898. private bool RestoreDeviceFromSavedState(ref DeviceState deviceState, InternedString layout)
  2899. {
  2900. // We assign the same device IDs here to newly created devices that they had
  2901. // before the domain reload. This is safe as device ID allocation is under the
  2902. // control of the runtime and not expected to be affected by a domain reload.
  2903. InputDevice device;
  2904. try
  2905. {
  2906. device = AddDevice(layout,
  2907. deviceDescription: deviceState.description,
  2908. deviceId: deviceState.deviceId,
  2909. deviceName: deviceState.name,
  2910. deviceFlags: deviceState.flags,
  2911. variants: new InternedString(deviceState.variants));
  2912. }
  2913. catch (Exception exception)
  2914. {
  2915. Debug.LogError(
  2916. $"Could not recreate input device '{deviceState.description}' with layout '{deviceState.layout}' and variants '{deviceState.variants}' after domain reload");
  2917. Debug.LogException(exception);
  2918. return true; // Don't try again.
  2919. }
  2920. deviceState.Restore(device);
  2921. return true;
  2922. }
  2923. #endif // UNITY_EDITOR || DEVELOPMENT_BUILD
  2924. }
  2925. }