Ei kuvausta
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

InputManager.cs 186KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using Unity.Collections;
  6. using UnityEngine.InputSystem.Composites;
  7. using UnityEngine.InputSystem.Controls;
  8. using Unity.Collections.LowLevel.Unsafe;
  9. using UnityEngine.Profiling;
  10. using UnityEngine.InputSystem.LowLevel;
  11. using UnityEngine.InputSystem.Processors;
  12. using UnityEngine.InputSystem.Interactions;
  13. using UnityEngine.InputSystem.Utilities;
  14. using UnityEngine.InputSystem.Layouts;
  15. #if UNITY_EDITOR
  16. using UnityEngine.InputSystem.Editor;
  17. #endif
  18. #if UNITY_EDITOR
  19. using CustomBindingPathValidator = System.Func<string, System.Action>;
  20. #endif
  21. ////TODO: make diagnostics available in dev players and give it a public API to enable them
  22. ////TODO: work towards InputManager having no direct knowledge of actions
  23. ////TODO: allow pushing events into the system any which way; decouple from the buffer in NativeInputSystem being the only source
  24. ////REVIEW: change the event properties over to using IObservable?
  25. ////REVIEW: instead of RegisterInteraction and RegisterProcessor, have a generic RegisterInterface (or something)?
  26. ////REVIEW: can we do away with the 'previous == previous frame' and simply buffer flip on every value write?
  27. ////REVIEW: should we force keeping mouse/pen/keyboard/touch around in editor even if not in list of supported devices?
  28. ////REVIEW: do we want to filter out state events that result in no state change?
  29. #pragma warning disable CS0649
  30. namespace UnityEngine.InputSystem
  31. {
  32. using DeviceChangeListener = Action<InputDevice, InputDeviceChange>;
  33. using DeviceStateChangeListener = Action<InputDevice, InputEventPtr>;
  34. using LayoutChangeListener = Action<string, InputControlLayoutChange>;
  35. using EventListener = Action<InputEventPtr, InputDevice>;
  36. using UpdateListener = Action;
  37. /// <summary>
  38. /// Hub of the input system.
  39. /// </summary>
  40. /// <remarks>
  41. /// Not exposed. Use <see cref="InputSystem"/> as the public entry point to the system.
  42. ///
  43. /// Manages devices, layouts, and event processing.
  44. /// </remarks>
  45. internal partial class InputManager
  46. {
  47. public ReadOnlyArray<InputDevice> devices => new ReadOnlyArray<InputDevice>(m_Devices, 0, m_DevicesCount);
  48. public TypeTable processors => m_Processors;
  49. public TypeTable interactions => m_Interactions;
  50. public TypeTable composites => m_Composites;
  51. public InputMetrics metrics
  52. {
  53. get
  54. {
  55. var result = m_Metrics;
  56. result.currentNumDevices = m_DevicesCount;
  57. result.currentStateSizeInBytes = (int)m_StateBuffers.totalSize;
  58. // Count controls.
  59. result.currentControlCount = m_DevicesCount;
  60. for (var i = 0; i < m_DevicesCount; ++i)
  61. result.currentControlCount += m_Devices[i].allControls.Count;
  62. // Count layouts.
  63. result.currentLayoutCount = m_Layouts.layoutTypes.Count;
  64. result.currentLayoutCount += m_Layouts.layoutStrings.Count;
  65. result.currentLayoutCount += m_Layouts.layoutBuilders.Count;
  66. result.currentLayoutCount += m_Layouts.layoutOverrides.Count;
  67. return result;
  68. }
  69. }
  70. public InputSettings settings
  71. {
  72. get
  73. {
  74. Debug.Assert(m_Settings != null);
  75. return m_Settings;
  76. }
  77. set
  78. {
  79. if (value == null)
  80. throw new ArgumentNullException(nameof(value));
  81. if (m_Settings == value)
  82. return;
  83. m_Settings = value;
  84. ApplySettings();
  85. }
  86. }
  87. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  88. public InputActionAsset actions
  89. {
  90. get
  91. {
  92. return m_Actions;
  93. }
  94. set
  95. {
  96. m_Actions = value;
  97. ApplyActions();
  98. }
  99. }
  100. #endif
  101. public InputUpdateType updateMask
  102. {
  103. get => m_UpdateMask;
  104. set
  105. {
  106. // In editor, we don't allow disabling editor updates.
  107. #if UNITY_EDITOR
  108. value |= InputUpdateType.Editor;
  109. #endif
  110. if (m_UpdateMask == value)
  111. return;
  112. m_UpdateMask = value;
  113. // Recreate state buffers.
  114. if (m_DevicesCount > 0)
  115. ReallocateStateBuffers();
  116. }
  117. }
  118. public InputUpdateType defaultUpdateType
  119. {
  120. get
  121. {
  122. if (m_CurrentUpdate != default)
  123. return m_CurrentUpdate;
  124. #if UNITY_EDITOR
  125. if (!m_RunPlayerUpdatesInEditMode && (!gameIsPlaying || !gameHasFocus))
  126. return InputUpdateType.Editor;
  127. #endif
  128. return m_UpdateMask.GetUpdateTypeForPlayer();
  129. }
  130. }
  131. public float pollingFrequency
  132. {
  133. get => m_PollingFrequency;
  134. set
  135. {
  136. ////REVIEW: allow setting to zero to turn off polling altogether?
  137. if (value <= 0)
  138. throw new ArgumentException("Polling frequency must be greater than zero", "value");
  139. m_PollingFrequency = value;
  140. if (m_Runtime != null)
  141. m_Runtime.pollingFrequency = value;
  142. }
  143. }
  144. public event DeviceChangeListener onDeviceChange
  145. {
  146. add => m_DeviceChangeListeners.AddCallback(value);
  147. remove => m_DeviceChangeListeners.RemoveCallback(value);
  148. }
  149. public event DeviceStateChangeListener onDeviceStateChange
  150. {
  151. add => m_DeviceStateChangeListeners.AddCallback(value);
  152. remove => m_DeviceStateChangeListeners.RemoveCallback(value);
  153. }
  154. public event InputDeviceCommandDelegate onDeviceCommand
  155. {
  156. add => m_DeviceCommandCallbacks.AddCallback(value);
  157. remove => m_DeviceCommandCallbacks.RemoveCallback(value);
  158. }
  159. ////REVIEW: would be great to have a way to sort out precedence between two callbacks
  160. public event InputDeviceFindControlLayoutDelegate onFindControlLayoutForDevice
  161. {
  162. add
  163. {
  164. m_DeviceFindLayoutCallbacks.AddCallback(value);
  165. // Having a new callback on this event can change the set of devices we recognize.
  166. // See if there's anything in the list of available devices that we can now turn
  167. // into an InputDevice whereas we couldn't before.
  168. //
  169. // NOTE: A callback could also impact already existing devices and theoretically alter
  170. // what layout we would have used for those. We do *NOT* retroactively apply
  171. // those changes.
  172. AddAvailableDevicesThatAreNowRecognized();
  173. }
  174. remove => m_DeviceFindLayoutCallbacks.RemoveCallback(value);
  175. }
  176. public event LayoutChangeListener onLayoutChange
  177. {
  178. add => m_LayoutChangeListeners.AddCallback(value);
  179. remove => m_LayoutChangeListeners.RemoveCallback(value);
  180. }
  181. ////TODO: add InputEventBuffer struct that uses NativeArray underneath
  182. ////TODO: make InputEventTrace use NativeArray
  183. ////TODO: introduce an alternative that consumes events in bulk
  184. public event EventListener onEvent
  185. {
  186. add => m_EventListeners.AddCallback(value);
  187. remove => m_EventListeners.RemoveCallback(value);
  188. }
  189. public event UpdateListener onBeforeUpdate
  190. {
  191. add
  192. {
  193. InstallBeforeUpdateHookIfNecessary();
  194. m_BeforeUpdateListeners.AddCallback(value);
  195. }
  196. remove => m_BeforeUpdateListeners.RemoveCallback(value);
  197. }
  198. public event UpdateListener onAfterUpdate
  199. {
  200. add => m_AfterUpdateListeners.AddCallback(value);
  201. remove => m_AfterUpdateListeners.RemoveCallback(value);
  202. }
  203. public event Action onSettingsChange
  204. {
  205. add => m_SettingsChangedListeners.AddCallback(value);
  206. remove => m_SettingsChangedListeners.RemoveCallback(value);
  207. }
  208. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  209. public event Action onActionsChange
  210. {
  211. add => m_ActionsChangedListeners.AddCallback(value);
  212. remove => m_ActionsChangedListeners.RemoveCallback(value);
  213. }
  214. #endif
  215. public bool isProcessingEvents => m_InputEventStream.isOpen;
  216. #if UNITY_EDITOR
  217. /// <summary>
  218. /// Callback that can be used to display a warning and draw additional custom Editor UI for bindings.
  219. /// </summary>
  220. /// <seealso cref="InputSystem.customBindingPathValidators"/>
  221. /// <remarks>
  222. /// This is not intended to be called directly.
  223. /// Please use <see cref="InputSystem.customBindingPathValidators"/> instead.
  224. /// </remarks>
  225. internal event CustomBindingPathValidator customBindingPathValidators
  226. {
  227. add => m_customBindingPathValidators.AddCallback(value);
  228. remove => m_customBindingPathValidators.RemoveCallback(value);
  229. }
  230. /// <summary>
  231. /// Invokes any custom UI rendering code for this Binding Path in the editor.
  232. /// </summary>
  233. /// <seealso cref="InputSystem.customBindingPathValidators"/>
  234. /// <remarks>
  235. /// This is not intended to be called directly.
  236. /// Please use <see cref="InputSystem.OnDrawCustomWarningForBindingPath"/> instead.
  237. /// </remarks>
  238. internal void OnDrawCustomWarningForBindingPath(string bindingPath)
  239. {
  240. DelegateHelpers.InvokeCallbacksSafe_AndInvokeReturnedActions(
  241. ref m_customBindingPathValidators,
  242. bindingPath,
  243. "InputSystem.OnDrawCustomWarningForBindingPath");
  244. }
  245. /// <summary>
  246. /// Determines if any warning icon is to be displayed for this Binding Path in the editor.
  247. /// </summary>
  248. /// <seealso cref="InputSystem.customBindingPathValidators"/>
  249. /// <remarks>
  250. /// This is not intended to be called directly.
  251. /// Please use <see cref="InputSystem.OnDrawCustomWarningForBindingPath"/> instead.
  252. /// </remarks>
  253. internal bool ShouldDrawWarningIconForBinding(string bindingPath)
  254. {
  255. return DelegateHelpers.InvokeCallbacksSafe_AnyCallbackReturnsObject(
  256. ref m_customBindingPathValidators,
  257. bindingPath,
  258. "InputSystem.ShouldDrawWarningIconForBinding");
  259. }
  260. #endif // UNITY_EDITOR
  261. #if UNITY_EDITOR
  262. private bool m_RunPlayerUpdatesInEditMode;
  263. /// <summary>
  264. /// If true, consider the editor to be in "perpetual play mode". Meaning, we ignore editor
  265. /// updates and just go and continuously process Dynamic/Fixed/BeforeRender regardless of
  266. /// whether we're in play mode or not.
  267. ///
  268. /// In this mode, we also ignore game view focus.
  269. /// </summary>
  270. public bool runPlayerUpdatesInEditMode
  271. {
  272. get => m_RunPlayerUpdatesInEditMode;
  273. set => m_RunPlayerUpdatesInEditMode = value;
  274. }
  275. #endif
  276. private bool gameIsPlaying =>
  277. #if UNITY_EDITOR
  278. (m_Runtime.isInPlayMode && !m_Runtime.isPaused) || m_RunPlayerUpdatesInEditMode;
  279. #else
  280. true;
  281. #endif
  282. private bool gameHasFocus =>
  283. #if UNITY_EDITOR
  284. m_RunPlayerUpdatesInEditMode || m_HasFocus || gameShouldGetInputRegardlessOfFocus;
  285. #else
  286. m_HasFocus || gameShouldGetInputRegardlessOfFocus;
  287. #endif
  288. private bool gameShouldGetInputRegardlessOfFocus =>
  289. m_Settings.backgroundBehavior == InputSettings.BackgroundBehavior.IgnoreFocus
  290. #if UNITY_EDITOR
  291. && m_Settings.editorInputBehaviorInPlayMode == InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView
  292. #endif
  293. ;
  294. ////TODO: when registering a layout that exists as a layout of a different type (type vs string vs constructor),
  295. //// remove the existing registration
  296. // Add a layout constructed from a type.
  297. // If a layout with the same name already exists, the new layout
  298. // takes its place.
  299. public void RegisterControlLayout(string name, Type type)
  300. {
  301. if (string.IsNullOrEmpty(name))
  302. throw new ArgumentNullException(nameof(name));
  303. if (type == null)
  304. throw new ArgumentNullException(nameof(type));
  305. // Note that since InputDevice derives from InputControl, isDeviceLayout implies
  306. // isControlLayout to be true as well.
  307. var isDeviceLayout = typeof(InputDevice).IsAssignableFrom(type);
  308. var isControlLayout = typeof(InputControl).IsAssignableFrom(type);
  309. if (!isDeviceLayout && !isControlLayout)
  310. throw new ArgumentException($"Types used as layouts have to be InputControls or InputDevices; '{type.Name}' is a '{type.BaseType.Name}'",
  311. nameof(type));
  312. var internedName = new InternedString(name);
  313. var isReplacement = m_Layouts.HasLayout(internedName);
  314. // All we do is enter the type into a map. We don't construct an InputControlLayout
  315. // from it until we actually need it in an InputDeviceBuilder to create a device.
  316. // This not only avoids us creating a bunch of objects on the managed heap but
  317. // also avoids us laboriously constructing a XRController layout, for example,
  318. // in a game that never uses XR.
  319. m_Layouts.layoutTypes[internedName] = type;
  320. ////TODO: make this independent of initialization order
  321. ////TODO: re-scan base type information after domain reloads
  322. // Walk class hierarchy all the way up to InputControl to see
  323. // if there's another type that's been registered as a layout.
  324. // If so, make it a base layout for this one.
  325. string baseLayout = null;
  326. for (var baseType = type.BaseType; baseLayout == null && baseType != typeof(InputControl);
  327. baseType = baseType.BaseType)
  328. {
  329. foreach (var entry in m_Layouts.layoutTypes)
  330. if (entry.Value == baseType)
  331. {
  332. baseLayout = entry.Key;
  333. break;
  334. }
  335. }
  336. PerformLayoutPostRegistration(internedName, new InlinedArray<InternedString>(new InternedString(baseLayout)),
  337. isReplacement, isKnownToBeDeviceLayout: isDeviceLayout);
  338. }
  339. public void RegisterControlLayout(string json, string name = null, bool isOverride = false)
  340. {
  341. if (string.IsNullOrEmpty(json))
  342. throw new ArgumentNullException(nameof(json));
  343. ////REVIEW: as long as no one has instantiated the layout, the base layout information is kinda pointless
  344. // Parse out name, device description, and base layout.
  345. InputControlLayout.ParseHeaderFieldsFromJson(json, out var nameFromJson, out var baseLayouts,
  346. out var deviceMatcher);
  347. // Decide whether to take name from JSON or from code.
  348. var internedLayoutName = new InternedString(name);
  349. if (internedLayoutName.IsEmpty())
  350. {
  351. internedLayoutName = nameFromJson;
  352. // Make sure we have a name.
  353. if (internedLayoutName.IsEmpty())
  354. throw new ArgumentException("Layout name has not been given and is not set in JSON layout",
  355. nameof(name));
  356. }
  357. // If it's an override, it must have a layout the overrides apply to.
  358. if (isOverride && baseLayouts.length == 0)
  359. {
  360. throw new ArgumentException(
  361. $"Layout override '{internedLayoutName}' must have 'extend' property mentioning layout to which to apply the overrides",
  362. nameof(json));
  363. }
  364. // Add it to our records.
  365. var isReplacement = m_Layouts.HasLayout(internedLayoutName);
  366. if (isReplacement && isOverride)
  367. { // Do not allow a layout override to replace a "base layout" by name, but allow layout overrides
  368. // to replace an existing layout override.
  369. // This is required to guarantee that its a hierarchy (directed graph) rather
  370. // than a cyclic graph.
  371. var isReplacingOverride = m_Layouts.layoutOverrideNames.Contains(internedLayoutName);
  372. if (!isReplacingOverride)
  373. {
  374. throw new ArgumentException($"Failed to register layout override '{internedLayoutName}'" +
  375. $"since a layout named '{internedLayoutName}' already exist. Layout overrides must " +
  376. $"have unique names with respect to existing layouts.");
  377. }
  378. }
  379. m_Layouts.layoutStrings[internedLayoutName] = json;
  380. if (isOverride)
  381. {
  382. m_Layouts.layoutOverrideNames.Add(internedLayoutName);
  383. for (var i = 0; i < baseLayouts.length; ++i)
  384. {
  385. var baseLayoutName = baseLayouts[i];
  386. m_Layouts.layoutOverrides.TryGetValue(baseLayoutName, out var overrideList);
  387. if (!isReplacement)
  388. ArrayHelpers.Append(ref overrideList, internedLayoutName);
  389. m_Layouts.layoutOverrides[baseLayoutName] = overrideList;
  390. }
  391. }
  392. PerformLayoutPostRegistration(internedLayoutName, baseLayouts,
  393. isReplacement: isReplacement, isOverride: isOverride);
  394. // If the layout contained a device matcher, register it.
  395. if (!deviceMatcher.empty)
  396. RegisterControlLayoutMatcher(internedLayoutName, deviceMatcher);
  397. }
  398. public void RegisterControlLayoutBuilder(Func<InputControlLayout> method, string name,
  399. string baseLayout = null)
  400. {
  401. if (method == null)
  402. throw new ArgumentNullException(nameof(method));
  403. if (string.IsNullOrEmpty(name))
  404. throw new ArgumentNullException(nameof(name));
  405. var internedLayoutName = new InternedString(name);
  406. var internedBaseLayoutName = new InternedString(baseLayout);
  407. var isReplacement = m_Layouts.HasLayout(internedLayoutName);
  408. m_Layouts.layoutBuilders[internedLayoutName] = method;
  409. PerformLayoutPostRegistration(internedLayoutName, new InlinedArray<InternedString>(internedBaseLayoutName),
  410. isReplacement);
  411. }
  412. private void PerformLayoutPostRegistration(InternedString layoutName, InlinedArray<InternedString> baseLayouts,
  413. bool isReplacement, bool isKnownToBeDeviceLayout = false, bool isOverride = false)
  414. {
  415. ++m_LayoutRegistrationVersion;
  416. // Force-clear layout cache. Don't clear reference count so that
  417. // the cache gets cleared out properly when released in case someone
  418. // is using it ATM.
  419. InputControlLayout.s_CacheInstance.Clear();
  420. // For layouts that aren't overrides, add the name of the base
  421. // layout to the lookup table.
  422. if (!isOverride && baseLayouts.length > 0)
  423. {
  424. if (baseLayouts.length > 1)
  425. throw new NotSupportedException(
  426. $"Layout '{layoutName}' has multiple base layouts; this is only supported on layout overrides");
  427. var baseLayoutName = baseLayouts[0];
  428. if (!baseLayoutName.IsEmpty())
  429. m_Layouts.baseLayoutTable[layoutName] = baseLayoutName;
  430. }
  431. // Nuke any precompiled layouts that are invalidated by the layout registration.
  432. m_Layouts.precompiledLayouts.Remove(layoutName);
  433. if (m_Layouts.precompiledLayouts.Count > 0)
  434. {
  435. foreach (var layout in m_Layouts.precompiledLayouts.Keys.ToArray())
  436. {
  437. var metadata = m_Layouts.precompiledLayouts[layout].metadata;
  438. // If it's an override, we remove any precompiled layouts to which overrides are applied.
  439. if (isOverride)
  440. {
  441. for (var i = 0; i < baseLayouts.length; ++i)
  442. if (layout == baseLayouts[i] ||
  443. StringHelpers.CharacterSeparatedListsHaveAtLeastOneCommonElement(metadata,
  444. baseLayouts[i], ';'))
  445. m_Layouts.precompiledLayouts.Remove(layout);
  446. }
  447. else
  448. {
  449. // Otherwise, we remove any precompile layouts that use the layout we just changed.
  450. if (StringHelpers.CharacterSeparatedListsHaveAtLeastOneCommonElement(metadata,
  451. layoutName, ';'))
  452. m_Layouts.precompiledLayouts.Remove(layout);
  453. }
  454. }
  455. }
  456. // Recreate any devices using the layout. If it's an override, recreate devices using any of the base layouts.
  457. if (isOverride)
  458. {
  459. for (var i = 0; i < baseLayouts.length; ++i)
  460. RecreateDevicesUsingLayout(baseLayouts[i], isKnownToBeDeviceLayout: isKnownToBeDeviceLayout);
  461. }
  462. else
  463. {
  464. RecreateDevicesUsingLayout(layoutName, isKnownToBeDeviceLayout: isKnownToBeDeviceLayout);
  465. }
  466. // In the editor, layouts may become available successively after a domain reload so
  467. // we may end up retaining device information all the way until we run the first full
  468. // player update. For every layout we register, we check here whether we have a saved
  469. // device state using a layout with the same name but not having a device description
  470. // (the latter is important as in that case, we should go through the normal matching
  471. // process and not just rely on the name of the layout). If so, we try here to recreate
  472. // the device with the just registered layout.
  473. #if UNITY_EDITOR
  474. for (var i = 0; i < m_SavedDeviceStates.LengthSafe(); ++i)
  475. {
  476. ref var deviceState = ref m_SavedDeviceStates[i];
  477. if (layoutName != deviceState.layout || !deviceState.description.empty)
  478. continue;
  479. if (RestoreDeviceFromSavedState(ref deviceState, layoutName))
  480. {
  481. ArrayHelpers.EraseAt(ref m_SavedDeviceStates, i);
  482. --i;
  483. }
  484. }
  485. #endif
  486. // Let listeners know.
  487. var change = isReplacement ? InputControlLayoutChange.Replaced : InputControlLayoutChange.Added;
  488. DelegateHelpers.InvokeCallbacksSafe(ref m_LayoutChangeListeners, layoutName.ToString(), change, "InputSystem.onLayoutChange");
  489. }
  490. public void RegisterPrecompiledLayout<TDevice>(string metadata)
  491. where TDevice : InputDevice, new()
  492. {
  493. if (metadata == null)
  494. throw new ArgumentNullException(nameof(metadata));
  495. var deviceType = typeof(TDevice).BaseType;
  496. var layoutName = FindOrRegisterDeviceLayoutForType(deviceType);
  497. m_Layouts.precompiledLayouts[layoutName] = new InputControlLayout.Collection.PrecompiledLayout
  498. {
  499. factoryMethod = () => new TDevice(),
  500. metadata = metadata
  501. };
  502. }
  503. private void RecreateDevicesUsingLayout(InternedString layout, bool isKnownToBeDeviceLayout = false)
  504. {
  505. if (m_DevicesCount == 0)
  506. return;
  507. List<InputDevice> devicesUsingLayout = null;
  508. // Find all devices using the layout.
  509. for (var i = 0; i < m_DevicesCount; ++i)
  510. {
  511. var device = m_Devices[i];
  512. bool usesLayout;
  513. if (isKnownToBeDeviceLayout)
  514. usesLayout = IsControlUsingLayout(device, layout);
  515. else
  516. usesLayout = IsControlOrChildUsingLayoutRecursive(device, layout);
  517. if (usesLayout)
  518. {
  519. if (devicesUsingLayout == null)
  520. devicesUsingLayout = new List<InputDevice>();
  521. devicesUsingLayout.Add(device);
  522. }
  523. }
  524. // If there's none, we're good.
  525. if (devicesUsingLayout == null)
  526. return;
  527. // Remove and re-add the matching devices.
  528. using (InputDeviceBuilder.Ref())
  529. {
  530. for (var i = 0; i < devicesUsingLayout.Count; ++i)
  531. {
  532. var device = devicesUsingLayout[i];
  533. RecreateDevice(device, device.m_Layout);
  534. }
  535. }
  536. }
  537. private bool IsControlOrChildUsingLayoutRecursive(InputControl control, InternedString layout)
  538. {
  539. // Check control itself.
  540. if (IsControlUsingLayout(control, layout))
  541. return true;
  542. // Check children.
  543. var children = control.children;
  544. for (var i = 0; i < children.Count; ++i)
  545. if (IsControlOrChildUsingLayoutRecursive(children[i], layout))
  546. return true;
  547. return false;
  548. }
  549. private bool IsControlUsingLayout(InputControl control, InternedString layout)
  550. {
  551. // Check direct match.
  552. if (control.layout == layout)
  553. return true;
  554. // Check base layout chain.
  555. var baseLayout = control.m_Layout;
  556. while (m_Layouts.baseLayoutTable.TryGetValue(baseLayout, out baseLayout))
  557. if (baseLayout == layout)
  558. return true;
  559. return false;
  560. }
  561. public void RegisterControlLayoutMatcher(string layoutName, InputDeviceMatcher matcher)
  562. {
  563. if (string.IsNullOrEmpty(layoutName))
  564. throw new ArgumentNullException(nameof(layoutName));
  565. if (matcher.empty)
  566. throw new ArgumentException("Matcher cannot be empty", nameof(matcher));
  567. // Add to table.
  568. var internedLayoutName = new InternedString(layoutName);
  569. m_Layouts.AddMatcher(internedLayoutName, matcher);
  570. // Recreate any device that we match better than its current layout.
  571. RecreateDevicesUsingLayoutWithInferiorMatch(matcher);
  572. // See if we can make sense of any device we couldn't make sense of before.
  573. AddAvailableDevicesMatchingDescription(matcher, internedLayoutName);
  574. }
  575. public void RegisterControlLayoutMatcher(Type type, InputDeviceMatcher matcher)
  576. {
  577. if (type == null)
  578. throw new ArgumentNullException(nameof(type));
  579. if (matcher.empty)
  580. throw new ArgumentException("Matcher cannot be empty", nameof(matcher));
  581. var layoutName = m_Layouts.TryFindLayoutForType(type);
  582. if (layoutName.IsEmpty())
  583. throw new ArgumentException(
  584. $"Type '{type.Name}' has not been registered as a control layout", nameof(type));
  585. RegisterControlLayoutMatcher(layoutName, matcher);
  586. }
  587. private void RecreateDevicesUsingLayoutWithInferiorMatch(InputDeviceMatcher deviceMatcher)
  588. {
  589. if (m_DevicesCount == 0)
  590. return;
  591. using (InputDeviceBuilder.Ref())
  592. {
  593. var deviceCount = m_DevicesCount;
  594. for (var i = 0; i < deviceCount; ++i)
  595. {
  596. var device = m_Devices[i];
  597. var deviceDescription = device.description;
  598. if (deviceDescription.empty || !(deviceMatcher.MatchPercentage(deviceDescription) > 0))
  599. continue;
  600. var layoutName = TryFindMatchingControlLayout(ref deviceDescription, device.deviceId);
  601. if (layoutName != device.m_Layout)
  602. {
  603. device.m_Description = deviceDescription;
  604. RecreateDevice(device, layoutName);
  605. // We're removing devices in the middle of the array and appending
  606. // them at the end. Adjust our index and device count to make sure
  607. // we're not iterating all the way into already processed devices.
  608. --i;
  609. --deviceCount;
  610. }
  611. }
  612. }
  613. }
  614. private void RecreateDevice(InputDevice oldDevice, InternedString newLayout)
  615. {
  616. // Remove.
  617. RemoveDevice(oldDevice, keepOnListOfAvailableDevices: true);
  618. // Re-setup device.
  619. var newDevice = InputDevice.Build<InputDevice>(newLayout, oldDevice.m_Variants,
  620. deviceDescription: oldDevice.m_Description);
  621. // Preserve device properties that should not be changed by the re-creation
  622. // of a device.
  623. newDevice.m_DeviceId = oldDevice.m_DeviceId;
  624. newDevice.m_Description = oldDevice.m_Description;
  625. if (oldDevice.native)
  626. newDevice.m_DeviceFlags |= InputDevice.DeviceFlags.Native;
  627. if (oldDevice.remote)
  628. newDevice.m_DeviceFlags |= InputDevice.DeviceFlags.Remote;
  629. if (!oldDevice.enabled)
  630. {
  631. newDevice.m_DeviceFlags |= InputDevice.DeviceFlags.DisabledStateHasBeenQueriedFromRuntime;
  632. newDevice.m_DeviceFlags |= InputDevice.DeviceFlags.DisabledInFrontend;
  633. }
  634. // Re-add.
  635. AddDevice(newDevice);
  636. }
  637. private void AddAvailableDevicesMatchingDescription(InputDeviceMatcher matcher, InternedString layout)
  638. {
  639. #if UNITY_EDITOR
  640. // If we still have some devices saved from the last domain reload, see
  641. // if they are matched by the given matcher. If so, turn them into devices.
  642. for (var i = 0; i < m_SavedDeviceStates.LengthSafe(); ++i)
  643. {
  644. ref var deviceState = ref m_SavedDeviceStates[i];
  645. if (matcher.MatchPercentage(deviceState.description) > 0)
  646. {
  647. RestoreDeviceFromSavedState(ref deviceState, layout);
  648. ArrayHelpers.EraseAt(ref m_SavedDeviceStates, i);
  649. --i;
  650. }
  651. }
  652. #endif
  653. // See if the new description to layout mapping allows us to make
  654. // sense of a device we couldn't make sense of so far.
  655. for (var i = 0; i < m_AvailableDeviceCount; ++i)
  656. {
  657. // Ignore if it's a device that has been explicitly removed.
  658. if (m_AvailableDevices[i].isRemoved)
  659. continue;
  660. var deviceId = m_AvailableDevices[i].deviceId;
  661. if (TryGetDeviceById(deviceId) != null)
  662. continue;
  663. if (matcher.MatchPercentage(m_AvailableDevices[i].description) > 0f)
  664. {
  665. // Try to create InputDevice instance.
  666. try
  667. {
  668. AddDevice(layout, deviceId, deviceDescription: m_AvailableDevices[i].description,
  669. deviceFlags: m_AvailableDevices[i].isNative ? InputDevice.DeviceFlags.Native : 0);
  670. }
  671. catch (Exception exception)
  672. {
  673. Debug.LogError(
  674. $"Layout '{layout}' matches existing device '{m_AvailableDevices[i].description}' but failed to instantiate: {exception}");
  675. Debug.LogException(exception);
  676. continue;
  677. }
  678. // Re-enable device.
  679. var command = EnableDeviceCommand.Create();
  680. m_Runtime.DeviceCommand(deviceId, ref command);
  681. }
  682. }
  683. }
  684. public void RemoveControlLayout(string name)
  685. {
  686. if (string.IsNullOrEmpty(name))
  687. throw new ArgumentNullException(nameof(name));
  688. var internedName = new InternedString(name);
  689. // Remove all devices using the layout.
  690. for (var i = 0; i < m_DevicesCount;)
  691. {
  692. var device = m_Devices[i];
  693. if (IsControlOrChildUsingLayoutRecursive(device, internedName))
  694. {
  695. RemoveDevice(device, keepOnListOfAvailableDevices: true);
  696. }
  697. else
  698. {
  699. ++i;
  700. }
  701. }
  702. // Remove layout record.
  703. m_Layouts.layoutTypes.Remove(internedName);
  704. m_Layouts.layoutStrings.Remove(internedName);
  705. m_Layouts.layoutBuilders.Remove(internedName);
  706. m_Layouts.baseLayoutTable.Remove(internedName);
  707. ++m_LayoutRegistrationVersion;
  708. ////TODO: check all layout inheritance chain for whether they are based on the layout and if so
  709. //// remove those layouts, too
  710. // Let listeners know.
  711. DelegateHelpers.InvokeCallbacksSafe(ref m_LayoutChangeListeners, name, InputControlLayoutChange.Removed, "InputSystem.onLayoutChange");
  712. }
  713. public InputControlLayout TryLoadControlLayout(Type type)
  714. {
  715. if (type == null)
  716. throw new ArgumentNullException(nameof(type));
  717. if (!typeof(InputControl).IsAssignableFrom(type))
  718. throw new ArgumentException($"Type '{type.Name}' is not an InputControl", nameof(type));
  719. // Find the layout name that the given type was registered with.
  720. var layoutName = m_Layouts.TryFindLayoutForType(type);
  721. if (layoutName.IsEmpty())
  722. throw new ArgumentException(
  723. $"Type '{type.Name}' has not been registered as a control layout", nameof(type));
  724. return m_Layouts.TryLoadLayout(layoutName);
  725. }
  726. public InputControlLayout TryLoadControlLayout(InternedString name)
  727. {
  728. return m_Layouts.TryLoadLayout(name);
  729. }
  730. ////FIXME: allowing the description to be modified as part of this is surprising; find a better way
  731. public InternedString TryFindMatchingControlLayout(ref InputDeviceDescription deviceDescription, int deviceId = InputDevice.InvalidDeviceId)
  732. {
  733. InternedString layoutName = new InternedString(string.Empty);
  734. try
  735. {
  736. Profiler.BeginSample("InputSystem.TryFindMatchingControlLayout");
  737. ////TODO: this will want to take overrides into account
  738. // See if we can match by description.
  739. layoutName = m_Layouts.TryFindMatchingLayout(deviceDescription);
  740. if (layoutName.IsEmpty())
  741. {
  742. // No, so try to match by device class. If we have a "Gamepad" layout,
  743. // for example, a device that classifies itself as a "Gamepad" will match
  744. // that layout.
  745. //
  746. // NOTE: Have to make sure here that we get a device layout and not a
  747. // control layout.
  748. if (!string.IsNullOrEmpty(deviceDescription.deviceClass))
  749. {
  750. var deviceClassLowerCase = new InternedString(deviceDescription.deviceClass);
  751. var type = m_Layouts.GetControlTypeForLayout(deviceClassLowerCase);
  752. if (type != null && typeof(InputDevice).IsAssignableFrom(type))
  753. layoutName = new InternedString(deviceDescription.deviceClass);
  754. }
  755. }
  756. ////REVIEW: listeners registering new layouts from in here may potentially lead to the creation of devices; should we disallow that?
  757. ////REVIEW: if a callback picks a layout, should we re-run through the list of callbacks? or should we just remove haveOverridenLayoutName?
  758. // Give listeners a shot to select/create a layout.
  759. if (m_DeviceFindLayoutCallbacks.length > 0)
  760. {
  761. // First time we get here, put our delegate for executing device commands
  762. // in place. We wrap the call to IInputRuntime.DeviceCommand so that we don't
  763. // need to expose the runtime to the onFindLayoutForDevice callbacks.
  764. if (m_DeviceFindExecuteCommandDelegate == null)
  765. m_DeviceFindExecuteCommandDelegate =
  766. (ref InputDeviceCommand commandRef) =>
  767. {
  768. if (m_DeviceFindExecuteCommandDeviceId == InputDevice.InvalidDeviceId)
  769. return InputDeviceCommand.GenericFailure;
  770. return m_Runtime.DeviceCommand(m_DeviceFindExecuteCommandDeviceId, ref commandRef);
  771. };
  772. m_DeviceFindExecuteCommandDeviceId = deviceId;
  773. var haveOverriddenLayoutName = false;
  774. m_DeviceFindLayoutCallbacks.LockForChanges();
  775. for (var i = 0; i < m_DeviceFindLayoutCallbacks.length; ++i)
  776. {
  777. try
  778. {
  779. var newLayout = m_DeviceFindLayoutCallbacks[i](ref deviceDescription, layoutName, m_DeviceFindExecuteCommandDelegate);
  780. if (!string.IsNullOrEmpty(newLayout) && !haveOverriddenLayoutName)
  781. {
  782. layoutName = new InternedString(newLayout);
  783. haveOverriddenLayoutName = true;
  784. }
  785. }
  786. catch (Exception exception)
  787. {
  788. Debug.LogError($"{exception.GetType().Name} while executing 'InputSystem.onFindLayoutForDevice' callbacks");
  789. Debug.LogException(exception);
  790. }
  791. }
  792. m_DeviceFindLayoutCallbacks.UnlockForChanges();
  793. }
  794. }
  795. finally
  796. {
  797. Profiler.EndSample();
  798. }
  799. return layoutName;
  800. }
  801. private InternedString FindOrRegisterDeviceLayoutForType(Type type)
  802. {
  803. var layoutName = m_Layouts.TryFindLayoutForType(type);
  804. if (layoutName.IsEmpty())
  805. {
  806. // Automatically register the given type as a layout.
  807. if (layoutName.IsEmpty())
  808. {
  809. layoutName = new InternedString(type.Name);
  810. RegisterControlLayout(type.Name, type);
  811. }
  812. }
  813. return layoutName;
  814. }
  815. /// <summary>
  816. /// Return true if the given device layout is supported by the game according to <see cref="InputSettings.supportedDevices"/>.
  817. /// </summary>
  818. /// <param name="layoutName">Name of the device layout.</param>
  819. /// <returns>True if a device with the given layout should be created for the game, false otherwise.</returns>
  820. private bool IsDeviceLayoutMarkedAsSupportedInSettings(InternedString layoutName)
  821. {
  822. // In the editor, "Supported Devices" can be overridden by a user setting. This causes
  823. // all available devices to be added regardless of what "Supported Devices" says. This
  824. // is useful to ensure that things like keyboard, mouse, and pen keep working in the editor
  825. // even if not supported as devices in the game.
  826. #if UNITY_EDITOR
  827. if (InputEditorUserSettings.addDevicesNotSupportedByProject)
  828. return true;
  829. #endif
  830. var supportedDevices = m_Settings.supportedDevices;
  831. if (supportedDevices.Count == 0)
  832. {
  833. // If supportedDevices is empty, all device layouts are considered supported.
  834. return true;
  835. }
  836. for (var n = 0; n < supportedDevices.Count; ++n)
  837. {
  838. var supportedLayout = new InternedString(supportedDevices[n]);
  839. if (layoutName == supportedLayout || m_Layouts.IsBasedOn(supportedLayout, layoutName))
  840. return true;
  841. }
  842. return false;
  843. }
  844. public IEnumerable<string> ListControlLayouts(string basedOn = null)
  845. {
  846. ////FIXME: this may add a name twice
  847. if (!string.IsNullOrEmpty(basedOn))
  848. {
  849. var internedBasedOn = new InternedString(basedOn);
  850. foreach (var entry in m_Layouts.layoutTypes)
  851. if (m_Layouts.IsBasedOn(internedBasedOn, entry.Key))
  852. yield return entry.Key;
  853. foreach (var entry in m_Layouts.layoutStrings)
  854. if (m_Layouts.IsBasedOn(internedBasedOn, entry.Key))
  855. yield return entry.Key;
  856. foreach (var entry in m_Layouts.layoutBuilders)
  857. if (m_Layouts.IsBasedOn(internedBasedOn, entry.Key))
  858. yield return entry.Key;
  859. }
  860. else
  861. {
  862. foreach (var entry in m_Layouts.layoutTypes)
  863. yield return entry.Key;
  864. foreach (var entry in m_Layouts.layoutStrings)
  865. yield return entry.Key;
  866. foreach (var entry in m_Layouts.layoutBuilders)
  867. yield return entry.Key;
  868. }
  869. }
  870. // Adds all controls that match the given path spec to the given list.
  871. // Returns number of controls added to the list.
  872. // NOTE: Does not create garbage.
  873. /// <summary>
  874. /// Adds to the given list all controls that match the given <see cref="InputControlPath">path spec</see>
  875. /// and are assignable to the given type.
  876. /// </summary>
  877. /// <param name="path"></param>
  878. /// <param name="controls"></param>
  879. /// <typeparam name="TControl"></typeparam>
  880. /// <returns></returns>
  881. public int GetControls<TControl>(string path, ref InputControlList<TControl> controls)
  882. where TControl : InputControl
  883. {
  884. if (string.IsNullOrEmpty(path))
  885. return 0;
  886. if (m_DevicesCount == 0)
  887. return 0;
  888. var deviceCount = m_DevicesCount;
  889. var numMatches = 0;
  890. for (var i = 0; i < deviceCount; ++i)
  891. {
  892. var device = m_Devices[i];
  893. numMatches += InputControlPath.TryFindControls(device, path, 0, ref controls);
  894. }
  895. return numMatches;
  896. }
  897. public void SetDeviceUsage(InputDevice device, InternedString usage)
  898. {
  899. if (device == null)
  900. throw new ArgumentNullException(nameof(device));
  901. if (device.usages.Count == 1 && device.usages[0] == usage)
  902. return;
  903. if (device.usages.Count == 0 && usage.IsEmpty())
  904. return;
  905. device.ClearDeviceUsages();
  906. if (!usage.IsEmpty())
  907. device.AddDeviceUsage(usage);
  908. NotifyUsageChanged(device);
  909. }
  910. public void AddDeviceUsage(InputDevice device, InternedString usage)
  911. {
  912. if (device == null)
  913. throw new ArgumentNullException(nameof(device));
  914. if (usage.IsEmpty())
  915. throw new ArgumentException("Usage string cannot be empty", nameof(usage));
  916. if (device.usages.Contains(usage))
  917. return;
  918. device.AddDeviceUsage(usage);
  919. NotifyUsageChanged(device);
  920. }
  921. public void RemoveDeviceUsage(InputDevice device, InternedString usage)
  922. {
  923. if (device == null)
  924. throw new ArgumentNullException(nameof(device));
  925. if (usage.IsEmpty())
  926. throw new ArgumentException("Usage string cannot be empty", nameof(usage));
  927. if (!device.usages.Contains(usage))
  928. return;
  929. device.RemoveDeviceUsage(usage);
  930. NotifyUsageChanged(device);
  931. }
  932. private void NotifyUsageChanged(InputDevice device)
  933. {
  934. InputActionState.OnDeviceChange(device, InputDeviceChange.UsageChanged);
  935. // Notify listeners.
  936. DelegateHelpers.InvokeCallbacksSafe(ref m_DeviceChangeListeners, device, InputDeviceChange.UsageChanged, "InputSystem.onDeviceChange");
  937. ////REVIEW: This was for the XRController leftHand and rightHand getters but these do lookups dynamically now; remove?
  938. // Usage may affect current device so update.
  939. device.MakeCurrent();
  940. }
  941. ////TODO: make sure that no device or control with a '/' in the name can creep into the system
  942. public InputDevice AddDevice(Type type, string name = null)
  943. {
  944. if (type == null)
  945. throw new ArgumentNullException(nameof(type));
  946. // Find the layout name that the given type was registered with.
  947. var layoutName = FindOrRegisterDeviceLayoutForType(type);
  948. Debug.Assert(!layoutName.IsEmpty(), name);
  949. // Note that since we go through the normal by-name lookup here, this will
  950. // still work if the layout from the type was override with a string layout.
  951. return AddDevice(layoutName, name);
  952. }
  953. // Creates a device from the given layout and adds it to the system.
  954. // NOTE: Creates garbage.
  955. public InputDevice AddDevice(string layout, string name = null, InternedString variants = new InternedString())
  956. {
  957. if (string.IsNullOrEmpty(layout))
  958. throw new ArgumentNullException(nameof(layout));
  959. var device = InputDevice.Build<InputDevice>(layout, variants);
  960. if (!string.IsNullOrEmpty(name))
  961. device.m_Name = new InternedString(name);
  962. AddDevice(device);
  963. return device;
  964. }
  965. // Add device with a forced ID. Used when creating devices reported to us by native.
  966. private InputDevice AddDevice(InternedString layout, int deviceId,
  967. string deviceName = null,
  968. InputDeviceDescription deviceDescription = new InputDeviceDescription(),
  969. InputDevice.DeviceFlags deviceFlags = 0,
  970. InternedString variants = default)
  971. {
  972. var device = InputDevice.Build<InputDevice>(new InternedString(layout),
  973. deviceDescription: deviceDescription,
  974. layoutVariants: variants);
  975. device.m_DeviceId = deviceId;
  976. device.m_Description = deviceDescription;
  977. device.m_DeviceFlags |= deviceFlags;
  978. if (!string.IsNullOrEmpty(deviceName))
  979. device.m_Name = new InternedString(deviceName);
  980. // Default display name to product name.
  981. if (!string.IsNullOrEmpty(deviceDescription.product))
  982. device.m_DisplayName = deviceDescription.product;
  983. AddDevice(device);
  984. return device;
  985. }
  986. public void AddDevice(InputDevice device)
  987. {
  988. if (device == null)
  989. throw new ArgumentNullException(nameof(device));
  990. if (string.IsNullOrEmpty(device.layout))
  991. throw new InvalidOperationException("Device has no associated layout");
  992. // Ignore if the same device gets added multiple times.
  993. if (ArrayHelpers.Contains(m_Devices, device))
  994. return;
  995. MakeDeviceNameUnique(device);
  996. AssignUniqueDeviceId(device);
  997. // Add to list.
  998. device.m_DeviceIndex = ArrayHelpers.AppendWithCapacity(ref m_Devices, ref m_DevicesCount, device);
  999. ////REVIEW: Not sure a full-blown dictionary is the right way here. Alternatives are to keep
  1000. //// a sparse array that directly indexes using the linearly increasing IDs (though that
  1001. //// may get large over time). Or to just do a linear search through m_Devices (but
  1002. //// that may end up tapping a bunch of memory locations in the heap to find the right
  1003. //// device; could be improved by sorting m_Devices by ID and picking a good starting
  1004. //// point based on the ID we have instead of searching from [0] always).
  1005. m_DevicesById[device.deviceId] = device;
  1006. // Let InputStateBuffers know this device doesn't have any associated state yet.
  1007. device.m_StateBlock.byteOffset = InputStateBlock.InvalidOffset;
  1008. // Update state buffers.
  1009. ReallocateStateBuffers();
  1010. InitializeDeviceState(device);
  1011. // Update metrics.
  1012. m_Metrics.maxNumDevices = Mathf.Max(m_DevicesCount, m_Metrics.maxNumDevices);
  1013. m_Metrics.maxStateSizeInBytes = Mathf.Max((int)m_StateBuffers.totalSize, m_Metrics.maxStateSizeInBytes);
  1014. // Make sure that if the device ID is listed in m_AvailableDevices, the device
  1015. // is no longer marked as removed.
  1016. for (var i = 0; i < m_AvailableDeviceCount; ++i)
  1017. {
  1018. if (m_AvailableDevices[i].deviceId == device.deviceId)
  1019. m_AvailableDevices[i].isRemoved = false;
  1020. }
  1021. // If we're running in the background, find out whether the device can run in
  1022. // the background. If not, disable it.
  1023. var isPlaying = true;
  1024. #if UNITY_EDITOR
  1025. isPlaying = m_Runtime.isInPlayMode;
  1026. #endif
  1027. if (isPlaying && !gameHasFocus
  1028. && m_Settings.backgroundBehavior != InputSettings.BackgroundBehavior.IgnoreFocus
  1029. && m_Runtime.runInBackground
  1030. && device.QueryEnabledStateFromRuntime()
  1031. && !ShouldRunDeviceInBackground(device))
  1032. {
  1033. EnableOrDisableDevice(device, false, DeviceDisableScope.TemporaryWhilePlayerIsInBackground);
  1034. }
  1035. ////REVIEW: we may want to suppress this during the initial device discovery phase
  1036. // Let actions re-resolve their paths.
  1037. InputActionState.OnDeviceChange(device, InputDeviceChange.Added);
  1038. // If the device wants automatic callbacks before input updates,
  1039. // put it on the list.
  1040. if (device is IInputUpdateCallbackReceiver beforeUpdateCallbackReceiver)
  1041. onBeforeUpdate += beforeUpdateCallbackReceiver.OnUpdate;
  1042. // If the device has state callbacks, make a note of it.
  1043. if (device is IInputStateCallbackReceiver)
  1044. {
  1045. InstallBeforeUpdateHookIfNecessary();
  1046. device.m_DeviceFlags |= InputDevice.DeviceFlags.HasStateCallbacks;
  1047. m_HaveDevicesWithStateCallbackReceivers = true;
  1048. }
  1049. // If the device has event merger, make a note of it.
  1050. if (device is IEventMerger)
  1051. device.hasEventMerger = true;
  1052. // If the device has event preprocessor, make a note of it.
  1053. if (device is IEventPreProcessor)
  1054. device.hasEventPreProcessor = true;
  1055. // If the device wants before-render updates, enable them if they
  1056. // aren't already.
  1057. if (device.updateBeforeRender)
  1058. updateMask |= InputUpdateType.BeforeRender;
  1059. // Notify device.
  1060. device.NotifyAdded();
  1061. ////REVIEW: is this really a good thing to do? just plugging in a device shouldn't make
  1062. //// it current, no?
  1063. // Make the device current.
  1064. // BEWARE: if this will not happen for whatever reason, you will break Android sensors,
  1065. // as they rely on .current for enabling native backend, see https://fogbugz.unity3d.com/f/cases/1371204/
  1066. device.MakeCurrent();
  1067. // Notify listeners.
  1068. DelegateHelpers.InvokeCallbacksSafe(ref m_DeviceChangeListeners, device, InputDeviceChange.Added, "InputSystem.onDeviceChange");
  1069. // Request device to send us an initial state update.
  1070. if (device.enabled)
  1071. device.RequestSync();
  1072. device.SetOptimizedControlDataTypeRecursively();
  1073. }
  1074. ////TODO: this path should really put the device on the list of available devices
  1075. ////TODO: this path should discover disconnected devices
  1076. public InputDevice AddDevice(InputDeviceDescription description)
  1077. {
  1078. ////REVIEW: is throwing here really such a useful thing?
  1079. return AddDevice(description, throwIfNoLayoutFound: true);
  1080. }
  1081. public InputDevice AddDevice(InputDeviceDescription description, bool throwIfNoLayoutFound,
  1082. string deviceName = null, int deviceId = InputDevice.InvalidDeviceId, InputDevice.DeviceFlags deviceFlags = 0)
  1083. {
  1084. Profiler.BeginSample("InputSystem.AddDevice");
  1085. // Look for matching layout.
  1086. var layout = TryFindMatchingControlLayout(ref description, deviceId);
  1087. // If no layout was found, bail out.
  1088. if (layout.IsEmpty())
  1089. {
  1090. if (throwIfNoLayoutFound)
  1091. {
  1092. Profiler.EndSample();
  1093. throw new ArgumentException($"Cannot find layout matching device description '{description}'", nameof(description));
  1094. }
  1095. // If it's a device coming from the runtime, disable it.
  1096. if (deviceId != InputDevice.InvalidDeviceId)
  1097. {
  1098. var command = DisableDeviceCommand.Create();
  1099. m_Runtime.DeviceCommand(deviceId, ref command);
  1100. }
  1101. Profiler.EndSample();
  1102. return null;
  1103. }
  1104. var device = AddDevice(layout, deviceId, deviceName, description, deviceFlags);
  1105. device.m_Description = description;
  1106. Profiler.EndSample();
  1107. return device;
  1108. }
  1109. public InputDevice AddDevice(InputDeviceDescription description, InternedString layout, string deviceName = null,
  1110. int deviceId = InputDevice.InvalidDeviceId, InputDevice.DeviceFlags deviceFlags = 0)
  1111. {
  1112. try
  1113. {
  1114. Profiler.BeginSample("InputSystem.AddDevice");
  1115. var device = AddDevice(layout, deviceId, deviceName, description, deviceFlags);
  1116. device.m_Description = description;
  1117. return device;
  1118. }
  1119. finally
  1120. {
  1121. Profiler.EndSample();
  1122. }
  1123. }
  1124. public void RemoveDevice(InputDevice device, bool keepOnListOfAvailableDevices = false)
  1125. {
  1126. if (device == null)
  1127. throw new ArgumentNullException(nameof(device));
  1128. // If device has not been added, ignore.
  1129. if (device.m_DeviceIndex == InputDevice.kInvalidDeviceIndex)
  1130. return;
  1131. // Remove state monitors while device index is still valid.
  1132. RemoveStateChangeMonitors(device);
  1133. // Remove from device array.
  1134. var deviceIndex = device.m_DeviceIndex;
  1135. var deviceId = device.deviceId;
  1136. if (deviceIndex < m_StateChangeMonitors.LengthSafe())
  1137. {
  1138. // m_StateChangeMonitors mirrors layout of m_Devices *but* may be shorter.
  1139. var count = m_StateChangeMonitors.Length;
  1140. ArrayHelpers.EraseAtWithCapacity(m_StateChangeMonitors, ref count, deviceIndex);
  1141. }
  1142. ArrayHelpers.EraseAtWithCapacity(m_Devices, ref m_DevicesCount, deviceIndex);
  1143. m_DevicesById.Remove(deviceId);
  1144. if (m_Devices != null)
  1145. {
  1146. // Remove from state buffers.
  1147. ReallocateStateBuffers();
  1148. }
  1149. else
  1150. {
  1151. // No more devices. Kill state buffers.
  1152. m_StateBuffers.FreeAll();
  1153. }
  1154. ////TODO: When we remove a native device like this, make sure we tell the backend to disable it (and re-enable it when re-add it)
  1155. // Update device indices. Do this after reallocating state buffers as that call requires
  1156. // the old indices to still be in place.
  1157. for (var i = deviceIndex; i < m_DevicesCount; ++i)
  1158. --m_Devices[i].m_DeviceIndex; // Indices have shifted down by one.
  1159. device.m_DeviceIndex = InputDevice.kInvalidDeviceIndex;
  1160. // Update list of available devices.
  1161. for (var i = 0; i < m_AvailableDeviceCount; ++i)
  1162. {
  1163. if (m_AvailableDevices[i].deviceId == deviceId)
  1164. {
  1165. if (keepOnListOfAvailableDevices)
  1166. m_AvailableDevices[i].isRemoved = true;
  1167. else
  1168. ArrayHelpers.EraseAtWithCapacity(m_AvailableDevices, ref m_AvailableDeviceCount, i);
  1169. break;
  1170. }
  1171. }
  1172. // Unbake offset into global state buffers.
  1173. device.BakeOffsetIntoStateBlockRecursive((uint)-device.m_StateBlock.byteOffset);
  1174. // Force enabled actions to remove controls from the device.
  1175. // We've already set the device index to be invalid so we any attempts
  1176. // by actions to uninstall state monitors will get ignored.
  1177. InputActionState.OnDeviceChange(device, InputDeviceChange.Removed);
  1178. // Kill before update callback, if applicable.
  1179. if (device is IInputUpdateCallbackReceiver beforeUpdateCallbackReceiver)
  1180. onBeforeUpdate -= beforeUpdateCallbackReceiver.OnUpdate;
  1181. // Disable before-render updates if this was the last device
  1182. // that requires them.
  1183. if (device.updateBeforeRender)
  1184. {
  1185. var haveDeviceRequiringBeforeRender = false;
  1186. for (var i = 0; i < m_DevicesCount; ++i)
  1187. if (m_Devices[i].updateBeforeRender)
  1188. {
  1189. haveDeviceRequiringBeforeRender = true;
  1190. break;
  1191. }
  1192. if (!haveDeviceRequiringBeforeRender)
  1193. updateMask &= ~InputUpdateType.BeforeRender;
  1194. }
  1195. // Let device know.
  1196. device.NotifyRemoved();
  1197. // Let listeners know.
  1198. DelegateHelpers.InvokeCallbacksSafe(ref m_DeviceChangeListeners, device, InputDeviceChange.Removed, "InputSystem.onDeviceChange");
  1199. // Try setting next device of same type as current
  1200. InputSystem.GetDevice(device.GetType())?.MakeCurrent();
  1201. }
  1202. public void FlushDisconnectedDevices()
  1203. {
  1204. m_DisconnectedDevices.Clear(m_DisconnectedDevicesCount);
  1205. m_DisconnectedDevicesCount = 0;
  1206. }
  1207. public unsafe void ResetDevice(InputDevice device, bool alsoResetDontResetControls = false, bool? issueResetCommand = null)
  1208. {
  1209. if (device == null)
  1210. throw new ArgumentNullException(nameof(device));
  1211. if (!device.added)
  1212. throw new InvalidOperationException($"Device '{device}' has not been added to the system");
  1213. var isHardReset = alsoResetDontResetControls || !device.hasDontResetControls;
  1214. // Trigger reset notification.
  1215. var change = isHardReset ? InputDeviceChange.HardReset : InputDeviceChange.SoftReset;
  1216. InputActionState.OnDeviceChange(device, change);
  1217. DelegateHelpers.InvokeCallbacksSafe(ref m_DeviceChangeListeners, device, change, "onDeviceChange");
  1218. // If the device implements its own reset, let it handle it.
  1219. if (!alsoResetDontResetControls && device is ICustomDeviceReset customReset)
  1220. {
  1221. customReset.Reset();
  1222. }
  1223. else
  1224. {
  1225. var defaultStatePtr = device.defaultStatePtr;
  1226. var deviceStateBlockSize = device.stateBlock.alignedSizeInBytes;
  1227. // Allocate temp memory to hold one state event.
  1228. ////REVIEW: the need for an event here is sufficiently obscure to warrant scrutiny; likely, there's a better way
  1229. //// to tell synthetic input (or input sources in general) apart
  1230. // NOTE: We wrap the reset in an artificial state event so that it appears to the rest of the system
  1231. // like any other input. If we don't do that but rather just call UpdateState() with a null event
  1232. // pointer, the change will be considered an internal state change and will get ignored by some
  1233. // pieces of code (such as EnhancedTouch which filters out internal state changes of Touchscreen
  1234. // by ignoring any change that is not coming from an input event).
  1235. using (var tempBuffer =
  1236. new NativeArray<byte>(InputEvent.kBaseEventSize + sizeof(int) + (int)deviceStateBlockSize, Allocator.Temp))
  1237. {
  1238. var stateEventPtr = (StateEvent*)tempBuffer.GetUnsafePtr();
  1239. var statePtr = stateEventPtr->state;
  1240. var currentTime = m_Runtime.currentTime;
  1241. // Set up the state event.
  1242. ref var stateBlock = ref device.m_StateBlock;
  1243. stateEventPtr->baseEvent.type = StateEvent.Type;
  1244. stateEventPtr->baseEvent.sizeInBytes = InputEvent.kBaseEventSize + sizeof(int) + deviceStateBlockSize;
  1245. stateEventPtr->baseEvent.time = currentTime;
  1246. stateEventPtr->baseEvent.deviceId = device.deviceId;
  1247. stateEventPtr->baseEvent.eventId = -1;
  1248. stateEventPtr->stateFormat = device.m_StateBlock.format;
  1249. // Decide whether we perform a soft reset or a hard reset.
  1250. if (isHardReset)
  1251. {
  1252. // Perform a hard reset where we wipe the entire device and set a full
  1253. // reset request to the backend.
  1254. UnsafeUtility.MemCpy(statePtr,
  1255. (byte*)defaultStatePtr + stateBlock.byteOffset,
  1256. deviceStateBlockSize);
  1257. }
  1258. else
  1259. {
  1260. // Perform a soft reset where we exclude any dontReset control (which is automatically
  1261. // toggled on for noisy controls) and do *NOT* send a reset request to the backend.
  1262. var currentStatePtr = device.currentStatePtr;
  1263. var resetMaskPtr = m_StateBuffers.resetMaskBuffer;
  1264. // To preserve values from dontReset controls, we need to first copy their current values.
  1265. UnsafeUtility.MemCpy(statePtr,
  1266. (byte*)currentStatePtr + stateBlock.byteOffset,
  1267. deviceStateBlockSize);
  1268. // And then we copy over default values masked by dontReset bits.
  1269. MemoryHelpers.MemCpyMasked(statePtr,
  1270. (byte*)defaultStatePtr + stateBlock.byteOffset,
  1271. (int)deviceStateBlockSize,
  1272. (byte*)resetMaskPtr + stateBlock.byteOffset);
  1273. }
  1274. UpdateState(device, defaultUpdateType, statePtr, 0, deviceStateBlockSize, currentTime,
  1275. new InputEventPtr((InputEvent*)stateEventPtr));
  1276. }
  1277. }
  1278. // In the editor, we don't want to issue RequestResetCommand to devices based on focus of the game view
  1279. // as this would also reset device state for the editor. And we don't need the reset commands in this case
  1280. // as -- unlike in the player --, Unity keeps running and we will keep seeing OS messages for these devices.
  1281. // So, in the editor, we generally suppress reset commands.
  1282. //
  1283. // The only exception is when the editor itself loses focus. We issue sync requests to all devices when
  1284. // coming back into focus. But for any device that doesn't support syncs, we actually do want to have a
  1285. // reset command reach the background.
  1286. //
  1287. // Finally, in the player, we also avoid reset commands when disabling a device as these are pointless.
  1288. // We sync/reset when enabling a device in the backend.
  1289. var doIssueResetCommand = isHardReset;
  1290. if (issueResetCommand != null)
  1291. doIssueResetCommand = issueResetCommand.Value;
  1292. #if UNITY_EDITOR
  1293. else if (m_Settings.editorInputBehaviorInPlayMode != InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView)
  1294. doIssueResetCommand = false;
  1295. #endif
  1296. if (doIssueResetCommand)
  1297. device.RequestReset();
  1298. }
  1299. public InputDevice TryGetDevice(string nameOrLayout)
  1300. {
  1301. if (string.IsNullOrEmpty(nameOrLayout))
  1302. throw new ArgumentException("Name is null or empty.", nameof(nameOrLayout));
  1303. if (m_DevicesCount == 0)
  1304. return null;
  1305. var nameOrLayoutLowerCase = nameOrLayout.ToLower();
  1306. for (var i = 0; i < m_DevicesCount; ++i)
  1307. {
  1308. var device = m_Devices[i];
  1309. if (device.m_Name.ToLower() == nameOrLayoutLowerCase ||
  1310. device.m_Layout.ToLower() == nameOrLayoutLowerCase)
  1311. return device;
  1312. }
  1313. return null;
  1314. }
  1315. public InputDevice GetDevice(string nameOrLayout)
  1316. {
  1317. var device = TryGetDevice(nameOrLayout);
  1318. if (device == null)
  1319. throw new ArgumentException($"Cannot find device with name or layout '{nameOrLayout}'", nameof(nameOrLayout));
  1320. return device;
  1321. }
  1322. public InputDevice TryGetDevice(Type layoutType)
  1323. {
  1324. var layoutName = m_Layouts.TryFindLayoutForType(layoutType);
  1325. if (layoutName.IsEmpty())
  1326. return null;
  1327. return TryGetDevice(layoutName);
  1328. }
  1329. public InputDevice TryGetDeviceById(int id)
  1330. {
  1331. if (m_DevicesById.TryGetValue(id, out var result))
  1332. return result;
  1333. return null;
  1334. }
  1335. // Adds any device that's been reported to the system but could not be matched to
  1336. // a layout to the given list.
  1337. public int GetUnsupportedDevices(List<InputDeviceDescription> descriptions)
  1338. {
  1339. if (descriptions == null)
  1340. throw new ArgumentNullException(nameof(descriptions));
  1341. var numFound = 0;
  1342. for (var i = 0; i < m_AvailableDeviceCount; ++i)
  1343. {
  1344. if (TryGetDeviceById(m_AvailableDevices[i].deviceId) != null)
  1345. continue;
  1346. descriptions.Add(m_AvailableDevices[i].description);
  1347. ++numFound;
  1348. }
  1349. return numFound;
  1350. }
  1351. // We have three different levels of disabling a device.
  1352. internal enum DeviceDisableScope
  1353. {
  1354. Everywhere, // Device is disabled globally and explicitly. Should neither send nor receive events.
  1355. InFrontendOnly, // Device is only disabled on managed side but not in backend. Should keep sending events but should not receive them (useful for redirecting their data).
  1356. TemporaryWhilePlayerIsInBackground, // Device has been disabled automatically and temporarily by system while application is running in the background.
  1357. }
  1358. public void EnableOrDisableDevice(InputDevice device, bool enable, DeviceDisableScope scope = default)
  1359. {
  1360. if (device == null)
  1361. throw new ArgumentNullException(nameof(device));
  1362. // Synchronize the enable/disabled state of the device.
  1363. if (enable)
  1364. {
  1365. ////REVIEW: Do we really want to allow overriding disabledWhileInBackground like it currently does?
  1366. // Enable device.
  1367. switch (scope)
  1368. {
  1369. case DeviceDisableScope.Everywhere:
  1370. device.disabledWhileInBackground = false;
  1371. if (!device.disabledInFrontend && !device.disabledInRuntime)
  1372. return;
  1373. if (device.disabledInRuntime)
  1374. {
  1375. device.ExecuteEnableCommand();
  1376. device.disabledInRuntime = false;
  1377. }
  1378. if (device.disabledInFrontend)
  1379. {
  1380. if (!device.RequestSync())
  1381. ResetDevice(device);
  1382. device.disabledInFrontend = false;
  1383. }
  1384. break;
  1385. case DeviceDisableScope.InFrontendOnly:
  1386. device.disabledWhileInBackground = false;
  1387. if (!device.disabledInFrontend && device.disabledInRuntime)
  1388. return;
  1389. if (!device.disabledInRuntime)
  1390. {
  1391. device.ExecuteDisableCommand();
  1392. device.disabledInRuntime = true;
  1393. }
  1394. if (device.disabledInFrontend)
  1395. {
  1396. if (!device.RequestSync())
  1397. ResetDevice(device);
  1398. device.disabledInFrontend = false;
  1399. }
  1400. break;
  1401. case DeviceDisableScope.TemporaryWhilePlayerIsInBackground:
  1402. if (device.disabledWhileInBackground)
  1403. {
  1404. if (device.disabledInRuntime)
  1405. {
  1406. device.ExecuteEnableCommand();
  1407. device.disabledInRuntime = false;
  1408. }
  1409. if (!device.RequestSync())
  1410. ResetDevice(device);
  1411. device.disabledWhileInBackground = false;
  1412. }
  1413. break;
  1414. }
  1415. }
  1416. else
  1417. {
  1418. // Disable device.
  1419. switch (scope)
  1420. {
  1421. case DeviceDisableScope.Everywhere:
  1422. device.disabledWhileInBackground = false;
  1423. if (device.disabledInFrontend && device.disabledInRuntime)
  1424. return;
  1425. if (!device.disabledInRuntime)
  1426. {
  1427. device.ExecuteDisableCommand();
  1428. device.disabledInRuntime = true;
  1429. }
  1430. if (!device.disabledInFrontend)
  1431. {
  1432. // When disabling a device, also issuing a reset in the backend is pointless.
  1433. ResetDevice(device, issueResetCommand: false);
  1434. device.disabledInFrontend = true;
  1435. }
  1436. break;
  1437. case DeviceDisableScope.InFrontendOnly:
  1438. device.disabledWhileInBackground = false;
  1439. if (!device.disabledInRuntime && device.disabledInFrontend)
  1440. return;
  1441. if (device.disabledInRuntime)
  1442. {
  1443. device.ExecuteEnableCommand();
  1444. device.disabledInRuntime = false;
  1445. }
  1446. if (!device.disabledInFrontend)
  1447. {
  1448. // When disabling a device, also issuing a reset in the backend is pointless.
  1449. ResetDevice(device, issueResetCommand: false);
  1450. device.disabledInFrontend = true;
  1451. }
  1452. break;
  1453. case DeviceDisableScope.TemporaryWhilePlayerIsInBackground:
  1454. // Won't flag a device as DisabledWhileInBackground if it is explicitly disabled in
  1455. // the frontend.
  1456. if (device.disabledInFrontend || device.disabledWhileInBackground)
  1457. return;
  1458. device.disabledWhileInBackground = true;
  1459. ResetDevice(device, issueResetCommand: false);
  1460. #if UNITY_EDITOR
  1461. if (m_Settings.editorInputBehaviorInPlayMode == InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView)
  1462. #endif
  1463. {
  1464. device.ExecuteDisableCommand();
  1465. device.disabledInRuntime = true;
  1466. }
  1467. break;
  1468. }
  1469. }
  1470. // Let listeners know.
  1471. var deviceChange = enable ? InputDeviceChange.Enabled : InputDeviceChange.Disabled;
  1472. DelegateHelpers.InvokeCallbacksSafe(ref m_DeviceChangeListeners, device, deviceChange, "InputSystem.onDeviceChange");
  1473. }
  1474. private unsafe void QueueEvent(InputEvent* eventPtr)
  1475. {
  1476. // If we're currently in OnUpdate(), the m_InputEventStream will be open. In that case,
  1477. // append events directly to that buffer and do *NOT* go into native.
  1478. if (m_InputEventStream.isOpen)
  1479. {
  1480. m_InputEventStream.Write(eventPtr);
  1481. return;
  1482. }
  1483. // Don't bother keeping the data on the managed side. Just stuff the raw data directly
  1484. // into the native buffers. This also means this method is thread-safe.
  1485. m_Runtime.QueueEvent(eventPtr);
  1486. }
  1487. public unsafe void QueueEvent(InputEventPtr ptr)
  1488. {
  1489. QueueEvent(ptr.data);
  1490. }
  1491. public unsafe void QueueEvent<TEvent>(ref TEvent inputEvent)
  1492. where TEvent : struct, IInputEventTypeInfo
  1493. {
  1494. QueueEvent((InputEvent*)UnsafeUtility.AddressOf(ref inputEvent));
  1495. }
  1496. public void Update()
  1497. {
  1498. Update(defaultUpdateType);
  1499. }
  1500. public void Update(InputUpdateType updateType)
  1501. {
  1502. m_Runtime.Update(updateType);
  1503. }
  1504. internal void Initialize(IInputRuntime runtime, InputSettings settings)
  1505. {
  1506. Debug.Assert(settings != null);
  1507. m_Settings = settings;
  1508. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  1509. InitializeActions();
  1510. #endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  1511. InitializeData();
  1512. InstallRuntime(runtime);
  1513. InstallGlobals();
  1514. ApplySettings();
  1515. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  1516. ApplyActions();
  1517. #endif
  1518. }
  1519. internal void Destroy()
  1520. {
  1521. // There isn't really much of a point in removing devices but we still
  1522. // want to clear out any global state they may be keeping. So just tell
  1523. // the devices that they got removed without actually removing them.
  1524. for (var i = 0; i < m_DevicesCount; ++i)
  1525. m_Devices[i].NotifyRemoved();
  1526. // Free all state memory.
  1527. m_StateBuffers.FreeAll();
  1528. // Uninstall globals.
  1529. UninstallGlobals();
  1530. // Destroy settings if they are temporary.
  1531. if (m_Settings != null && m_Settings.hideFlags == HideFlags.HideAndDontSave)
  1532. Object.DestroyImmediate(m_Settings);
  1533. // Project-wide Actions are never temporary so we do not destroy them.
  1534. }
  1535. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  1536. // Initialize project-wide actions:
  1537. // - In editor (edit mode or play-mode) we always use the editor build preferences persisted setting.
  1538. // - In player build we always attempt to find a preloaded asset.
  1539. private void InitializeActions()
  1540. {
  1541. #if UNITY_EDITOR
  1542. m_Actions = ProjectWideActionsBuildProvider.actionsToIncludeInPlayerBuild;
  1543. #else
  1544. m_Actions = null;
  1545. var candidates = Resources.FindObjectsOfTypeAll<InputActionAsset>();
  1546. foreach (var candidate in candidates)
  1547. {
  1548. if (candidate.m_IsProjectWide)
  1549. {
  1550. m_Actions = candidate;
  1551. break;
  1552. }
  1553. }
  1554. #endif // UNITY_EDITOR
  1555. }
  1556. #endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  1557. internal void InitializeData()
  1558. {
  1559. m_Layouts.Allocate();
  1560. m_Processors.Initialize();
  1561. m_Interactions.Initialize();
  1562. m_Composites.Initialize();
  1563. m_DevicesById = new Dictionary<int, InputDevice>();
  1564. // Determine our default set of enabled update types. By
  1565. // default we enable both fixed and dynamic update because
  1566. // we don't know which one the user is going to use. The user
  1567. // can manually turn off one of them to optimize operation.
  1568. m_UpdateMask = InputUpdateType.Dynamic | InputUpdateType.Fixed;
  1569. m_HasFocus = Application.isFocused;
  1570. #if UNITY_EDITOR
  1571. m_EditorIsActive = true;
  1572. m_UpdateMask |= InputUpdateType.Editor;
  1573. #endif
  1574. // Default polling frequency is 60 Hz.
  1575. m_PollingFrequency = 60;
  1576. // Register layouts.
  1577. // NOTE: Base layouts must be registered before their derived layouts
  1578. // for the detection of base layouts to work.
  1579. RegisterControlLayout("Axis", typeof(AxisControl)); // Controls.
  1580. RegisterControlLayout("Button", typeof(ButtonControl));
  1581. RegisterControlLayout("DiscreteButton", typeof(DiscreteButtonControl));
  1582. RegisterControlLayout("Key", typeof(KeyControl));
  1583. RegisterControlLayout("Analog", typeof(AxisControl));
  1584. RegisterControlLayout("Integer", typeof(IntegerControl));
  1585. RegisterControlLayout("Digital", typeof(IntegerControl));
  1586. RegisterControlLayout("Double", typeof(DoubleControl));
  1587. RegisterControlLayout("Vector2", typeof(Vector2Control));
  1588. RegisterControlLayout("Vector3", typeof(Vector3Control));
  1589. RegisterControlLayout("Delta", typeof(DeltaControl));
  1590. RegisterControlLayout("Quaternion", typeof(QuaternionControl));
  1591. RegisterControlLayout("Stick", typeof(StickControl));
  1592. RegisterControlLayout("Dpad", typeof(DpadControl));
  1593. RegisterControlLayout("DpadAxis", typeof(DpadControl.DpadAxisControl));
  1594. RegisterControlLayout("AnyKey", typeof(AnyKeyControl));
  1595. RegisterControlLayout("Touch", typeof(TouchControl));
  1596. RegisterControlLayout("TouchPhase", typeof(TouchPhaseControl));
  1597. RegisterControlLayout("TouchPress", typeof(TouchPressControl));
  1598. RegisterControlLayout("Gamepad", typeof(Gamepad)); // Devices.
  1599. RegisterControlLayout("Joystick", typeof(Joystick));
  1600. RegisterControlLayout("Keyboard", typeof(Keyboard));
  1601. RegisterControlLayout("Pointer", typeof(Pointer));
  1602. RegisterControlLayout("Mouse", typeof(Mouse));
  1603. RegisterControlLayout("Pen", typeof(Pen));
  1604. RegisterControlLayout("Touchscreen", typeof(Touchscreen));
  1605. RegisterControlLayout("Sensor", typeof(Sensor));
  1606. RegisterControlLayout("Accelerometer", typeof(Accelerometer));
  1607. RegisterControlLayout("Gyroscope", typeof(Gyroscope));
  1608. RegisterControlLayout("GravitySensor", typeof(GravitySensor));
  1609. RegisterControlLayout("AttitudeSensor", typeof(AttitudeSensor));
  1610. RegisterControlLayout("LinearAccelerationSensor", typeof(LinearAccelerationSensor));
  1611. RegisterControlLayout("MagneticFieldSensor", typeof(MagneticFieldSensor));
  1612. RegisterControlLayout("LightSensor", typeof(LightSensor));
  1613. RegisterControlLayout("PressureSensor", typeof(PressureSensor));
  1614. RegisterControlLayout("HumiditySensor", typeof(HumiditySensor));
  1615. RegisterControlLayout("AmbientTemperatureSensor", typeof(AmbientTemperatureSensor));
  1616. RegisterControlLayout("StepCounter", typeof(StepCounter));
  1617. RegisterControlLayout("TrackedDevice", typeof(TrackedDevice));
  1618. // Precompiled layouts.
  1619. RegisterPrecompiledLayout<FastKeyboard>(FastKeyboard.metadata);
  1620. RegisterPrecompiledLayout<FastTouchscreen>(FastTouchscreen.metadata);
  1621. RegisterPrecompiledLayout<FastMouse>(FastMouse.metadata);
  1622. // Register processors.
  1623. processors.AddTypeRegistration("Invert", typeof(InvertProcessor));
  1624. processors.AddTypeRegistration("InvertVector2", typeof(InvertVector2Processor));
  1625. processors.AddTypeRegistration("InvertVector3", typeof(InvertVector3Processor));
  1626. processors.AddTypeRegistration("Clamp", typeof(ClampProcessor));
  1627. processors.AddTypeRegistration("Normalize", typeof(NormalizeProcessor));
  1628. processors.AddTypeRegistration("NormalizeVector2", typeof(NormalizeVector2Processor));
  1629. processors.AddTypeRegistration("NormalizeVector3", typeof(NormalizeVector3Processor));
  1630. processors.AddTypeRegistration("Scale", typeof(ScaleProcessor));
  1631. processors.AddTypeRegistration("ScaleVector2", typeof(ScaleVector2Processor));
  1632. processors.AddTypeRegistration("ScaleVector3", typeof(ScaleVector3Processor));
  1633. processors.AddTypeRegistration("StickDeadzone", typeof(StickDeadzoneProcessor));
  1634. processors.AddTypeRegistration("AxisDeadzone", typeof(AxisDeadzoneProcessor));
  1635. processors.AddTypeRegistration("CompensateDirection", typeof(CompensateDirectionProcessor));
  1636. processors.AddTypeRegistration("CompensateRotation", typeof(CompensateRotationProcessor));
  1637. #if UNITY_EDITOR
  1638. processors.AddTypeRegistration("AutoWindowSpace", typeof(EditorWindowSpaceProcessor));
  1639. #endif
  1640. // Register interactions.
  1641. interactions.AddTypeRegistration("Hold", typeof(HoldInteraction));
  1642. interactions.AddTypeRegistration("Tap", typeof(TapInteraction));
  1643. interactions.AddTypeRegistration("SlowTap", typeof(SlowTapInteraction));
  1644. interactions.AddTypeRegistration("MultiTap", typeof(MultiTapInteraction));
  1645. interactions.AddTypeRegistration("Press", typeof(PressInteraction));
  1646. // Register composites.
  1647. composites.AddTypeRegistration("1DAxis", typeof(AxisComposite));
  1648. composites.AddTypeRegistration("2DVector", typeof(Vector2Composite));
  1649. composites.AddTypeRegistration("3DVector", typeof(Vector3Composite));
  1650. composites.AddTypeRegistration("Axis", typeof(AxisComposite));// Alias for pre-0.2 name.
  1651. composites.AddTypeRegistration("Dpad", typeof(Vector2Composite));// Alias for pre-0.2 name.
  1652. composites.AddTypeRegistration("ButtonWithOneModifier", typeof(ButtonWithOneModifier));
  1653. composites.AddTypeRegistration("ButtonWithTwoModifiers", typeof(ButtonWithTwoModifiers));
  1654. composites.AddTypeRegistration("OneModifier", typeof(OneModifierComposite));
  1655. composites.AddTypeRegistration("TwoModifiers", typeof(TwoModifiersComposite));
  1656. }
  1657. internal void InstallRuntime(IInputRuntime runtime)
  1658. {
  1659. if (m_Runtime != null)
  1660. {
  1661. m_Runtime.onUpdate = null;
  1662. m_Runtime.onBeforeUpdate = null;
  1663. m_Runtime.onDeviceDiscovered = null;
  1664. m_Runtime.onPlayerFocusChanged = null;
  1665. m_Runtime.onShouldRunUpdate = null;
  1666. #if UNITY_EDITOR
  1667. m_Runtime.onPlayerLoopInitialization = null;
  1668. #endif
  1669. }
  1670. m_Runtime = runtime;
  1671. m_Runtime.onUpdate = OnUpdate;
  1672. m_Runtime.onDeviceDiscovered = OnNativeDeviceDiscovered;
  1673. m_Runtime.onPlayerFocusChanged = OnFocusChanged;
  1674. m_Runtime.onShouldRunUpdate = ShouldRunUpdate;
  1675. #if UNITY_EDITOR
  1676. m_Runtime.onPlayerLoopInitialization = OnPlayerLoopInitialization;
  1677. #endif
  1678. m_Runtime.pollingFrequency = pollingFrequency;
  1679. m_HasFocus = m_Runtime.isPlayerFocused;
  1680. // We only hook NativeInputSystem.onBeforeUpdate if necessary.
  1681. if (m_BeforeUpdateListeners.length > 0 || m_HaveDevicesWithStateCallbackReceivers)
  1682. {
  1683. m_Runtime.onBeforeUpdate = OnBeforeUpdate;
  1684. m_NativeBeforeUpdateHooked = true;
  1685. }
  1686. #if UNITY_ANALYTICS || UNITY_EDITOR
  1687. InputAnalytics.Initialize(this);
  1688. m_Runtime.onShutdown = () => InputAnalytics.OnShutdown(this);
  1689. #endif
  1690. }
  1691. internal void InstallGlobals()
  1692. {
  1693. Debug.Assert(m_Runtime != null);
  1694. InputControlLayout.s_Layouts = m_Layouts;
  1695. InputProcessor.s_Processors = m_Processors;
  1696. InputInteraction.s_Interactions = m_Interactions;
  1697. InputBindingComposite.s_Composites = m_Composites;
  1698. InputRuntime.s_Instance = m_Runtime;
  1699. InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup =
  1700. m_Runtime.currentTimeOffsetToRealtimeSinceStartup;
  1701. // Reset update state.
  1702. InputUpdate.Restore(new InputUpdate.SerializedState());
  1703. unsafe
  1704. {
  1705. InputStateBuffers.SwitchTo(m_StateBuffers, InputUpdateType.Dynamic);
  1706. InputStateBuffers.s_DefaultStateBuffer = m_StateBuffers.defaultStateBuffer;
  1707. InputStateBuffers.s_NoiseMaskBuffer = m_StateBuffers.noiseMaskBuffer;
  1708. InputStateBuffers.s_ResetMaskBuffer = m_StateBuffers.resetMaskBuffer;
  1709. }
  1710. }
  1711. internal void UninstallGlobals()
  1712. {
  1713. if (ReferenceEquals(InputControlLayout.s_Layouts.baseLayoutTable, m_Layouts.baseLayoutTable))
  1714. InputControlLayout.s_Layouts = new InputControlLayout.Collection();
  1715. if (ReferenceEquals(InputProcessor.s_Processors.table, m_Processors.table))
  1716. InputProcessor.s_Processors = new TypeTable();
  1717. if (ReferenceEquals(InputInteraction.s_Interactions.table, m_Interactions.table))
  1718. InputInteraction.s_Interactions = new TypeTable();
  1719. if (ReferenceEquals(InputBindingComposite.s_Composites.table, m_Composites.table))
  1720. InputBindingComposite.s_Composites = new TypeTable();
  1721. // Clear layout cache.
  1722. InputControlLayout.s_CacheInstance = default;
  1723. InputControlLayout.s_CacheInstanceRef = 0;
  1724. // Detach from runtime.
  1725. if (m_Runtime != null)
  1726. {
  1727. m_Runtime.onUpdate = null;
  1728. m_Runtime.onDeviceDiscovered = null;
  1729. m_Runtime.onBeforeUpdate = null;
  1730. m_Runtime.onPlayerFocusChanged = null;
  1731. m_Runtime.onShouldRunUpdate = null;
  1732. if (ReferenceEquals(InputRuntime.s_Instance, m_Runtime))
  1733. InputRuntime.s_Instance = null;
  1734. }
  1735. }
  1736. [Serializable]
  1737. internal struct AvailableDevice
  1738. {
  1739. public InputDeviceDescription description;
  1740. public int deviceId;
  1741. public bool isNative;
  1742. public bool isRemoved;
  1743. }
  1744. // Used by EditorInputControlLayoutCache to determine whether its state is outdated.
  1745. internal int m_LayoutRegistrationVersion;
  1746. private float m_PollingFrequency;
  1747. internal InputControlLayout.Collection m_Layouts;
  1748. private TypeTable m_Processors;
  1749. private TypeTable m_Interactions;
  1750. private TypeTable m_Composites;
  1751. private int m_DevicesCount;
  1752. private InputDevice[] m_Devices;
  1753. private Dictionary<int, InputDevice> m_DevicesById;
  1754. internal int m_AvailableDeviceCount;
  1755. internal AvailableDevice[] m_AvailableDevices; // A record of all devices reported to the system (from native or user code).
  1756. ////REVIEW: should these be weak-referenced?
  1757. internal int m_DisconnectedDevicesCount;
  1758. internal InputDevice[] m_DisconnectedDevices;
  1759. internal InputUpdateType m_UpdateMask; // Which of our update types are enabled.
  1760. private InputUpdateType m_CurrentUpdate;
  1761. internal InputStateBuffers m_StateBuffers;
  1762. #if UNITY_EDITOR
  1763. // remember time offset to correctly restore it after editor mode is done
  1764. private double latestNonEditorTimeOffsetToRealtimeSinceStartup;
  1765. #endif
  1766. // We don't use UnityEvents and thus don't persist the callbacks during domain reloads.
  1767. // Restoration of UnityActions is unreliable and it's too easy to end up with double
  1768. // registrations what will lead to all kinds of misbehavior.
  1769. private CallbackArray<DeviceChangeListener> m_DeviceChangeListeners;
  1770. private CallbackArray<DeviceStateChangeListener> m_DeviceStateChangeListeners;
  1771. private CallbackArray<InputDeviceFindControlLayoutDelegate> m_DeviceFindLayoutCallbacks;
  1772. internal CallbackArray<InputDeviceCommandDelegate> m_DeviceCommandCallbacks;
  1773. private CallbackArray<LayoutChangeListener> m_LayoutChangeListeners;
  1774. private CallbackArray<EventListener> m_EventListeners;
  1775. private CallbackArray<UpdateListener> m_BeforeUpdateListeners;
  1776. private CallbackArray<UpdateListener> m_AfterUpdateListeners;
  1777. private CallbackArray<Action> m_SettingsChangedListeners;
  1778. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  1779. private CallbackArray<Action> m_ActionsChangedListeners;
  1780. #endif
  1781. private bool m_NativeBeforeUpdateHooked;
  1782. private bool m_HaveDevicesWithStateCallbackReceivers;
  1783. private bool m_HasFocus;
  1784. private InputEventStream m_InputEventStream;
  1785. // We want to sync devices when the editor comes back into focus. Unfortunately, there's no
  1786. // callback for this so we have to poll this state.
  1787. #if UNITY_EDITOR
  1788. private bool m_EditorIsActive;
  1789. #endif
  1790. // Allow external users to hook in validators and draw custom UI in the binding path editor
  1791. #if UNITY_EDITOR
  1792. private Utilities.CallbackArray<CustomBindingPathValidator> m_customBindingPathValidators;
  1793. #endif
  1794. // We allocate the 'executeDeviceCommand' closure passed to 'onFindLayoutForDevice'
  1795. // only once to avoid creating garbage.
  1796. private InputDeviceExecuteCommandDelegate m_DeviceFindExecuteCommandDelegate;
  1797. private int m_DeviceFindExecuteCommandDeviceId;
  1798. #if UNITY_ANALYTICS || UNITY_EDITOR
  1799. private bool m_HaveSentStartupAnalytics;
  1800. #endif
  1801. internal IInputRuntime m_Runtime;
  1802. internal InputMetrics m_Metrics;
  1803. internal InputSettings m_Settings;
  1804. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  1805. private InputActionAsset m_Actions;
  1806. #endif
  1807. #if UNITY_EDITOR
  1808. internal IInputDiagnostics m_Diagnostics;
  1809. #endif
  1810. ////REVIEW: Make it so that device names *always* have a number appended? (i.e. Gamepad1, Gamepad2, etc. instead of Gamepad, Gamepad1, etc)
  1811. private void MakeDeviceNameUnique(InputDevice device)
  1812. {
  1813. if (m_DevicesCount == 0)
  1814. return;
  1815. var deviceName = StringHelpers.MakeUniqueName(device.name, m_Devices, x => x != null ? x.name : string.Empty);
  1816. if (deviceName != device.name)
  1817. {
  1818. // If we have changed the name of the device, nuke all path strings in the control
  1819. // hierarchy so that they will get re-recreated when queried.
  1820. ResetControlPathsRecursive(device);
  1821. // Assign name.
  1822. device.m_Name = new InternedString(deviceName);
  1823. }
  1824. }
  1825. private static void ResetControlPathsRecursive(InputControl control)
  1826. {
  1827. control.m_Path = null;
  1828. var children = control.children;
  1829. var childCount = children.Count;
  1830. for (var i = 0; i < childCount; ++i)
  1831. ResetControlPathsRecursive(children[i]);
  1832. }
  1833. private void AssignUniqueDeviceId(InputDevice device)
  1834. {
  1835. // If the device already has an ID, make sure it's unique.
  1836. if (device.deviceId != InputDevice.InvalidDeviceId)
  1837. {
  1838. // Safety check to make sure out IDs are really unique.
  1839. // Given they are assigned by the native system they should be fine
  1840. // but let's make sure.
  1841. var existingDeviceWithId = TryGetDeviceById(device.deviceId);
  1842. if (existingDeviceWithId != null)
  1843. throw new InvalidOperationException(
  1844. $"Duplicate device ID {device.deviceId} detected for devices '{device.name}' and '{existingDeviceWithId.name}'");
  1845. }
  1846. else
  1847. {
  1848. device.m_DeviceId = m_Runtime.AllocateDeviceId();
  1849. }
  1850. }
  1851. // (Re)allocates state buffers and assigns each device that's been added
  1852. // a segment of the buffer. Preserves the current state of devices.
  1853. // NOTE: Installs the buffers globally.
  1854. private unsafe void ReallocateStateBuffers()
  1855. {
  1856. var oldBuffers = m_StateBuffers;
  1857. // Allocate new buffers.
  1858. var newBuffers = new InputStateBuffers();
  1859. newBuffers.AllocateAll(m_Devices, m_DevicesCount);
  1860. // Migrate state.
  1861. newBuffers.MigrateAll(m_Devices, m_DevicesCount, oldBuffers);
  1862. // Install the new buffers.
  1863. oldBuffers.FreeAll();
  1864. m_StateBuffers = newBuffers;
  1865. InputStateBuffers.s_DefaultStateBuffer = newBuffers.defaultStateBuffer;
  1866. InputStateBuffers.s_NoiseMaskBuffer = newBuffers.noiseMaskBuffer;
  1867. InputStateBuffers.s_ResetMaskBuffer = newBuffers.resetMaskBuffer;
  1868. // Switch to buffers.
  1869. InputStateBuffers.SwitchTo(m_StateBuffers,
  1870. InputUpdate.s_LatestUpdateType != InputUpdateType.None ? InputUpdate.s_LatestUpdateType : defaultUpdateType);
  1871. ////TODO: need to update state change monitors
  1872. }
  1873. /// <summary>
  1874. /// Initialize default state for given device.
  1875. /// </summary>
  1876. /// <param name="device">A newly added input device.</param>
  1877. /// <remarks>
  1878. /// For every device, one copy of its state is kept around which is initialized with the default
  1879. /// values for the device. If the device has no control that has an explicitly specified control
  1880. /// value, the buffer simply contains all zeroes.
  1881. ///
  1882. /// The default state buffer is initialized once when a device is added to the system and then
  1883. /// migrated by <see cref="InputStateBuffers"/> like other device state and removed when the device
  1884. /// is removed from the system.
  1885. /// </remarks>
  1886. private unsafe void InitializeDefaultState(InputDevice device)
  1887. {
  1888. // Nothing to do if device has a default state of all zeroes.
  1889. if (!device.hasControlsWithDefaultState)
  1890. return;
  1891. // Otherwise go through each control and write its default value.
  1892. var controls = device.allControls;
  1893. var controlCount = controls.Count;
  1894. var defaultStateBuffer = m_StateBuffers.defaultStateBuffer;
  1895. for (var n = 0; n < controlCount; ++n)
  1896. {
  1897. var control = controls[n];
  1898. if (!control.hasDefaultState)
  1899. continue;
  1900. control.m_StateBlock.Write(defaultStateBuffer, control.m_DefaultState);
  1901. }
  1902. // Copy default state to all front and back buffers.
  1903. var stateBlock = device.m_StateBlock;
  1904. var deviceIndex = device.m_DeviceIndex;
  1905. if (m_StateBuffers.m_PlayerStateBuffers.valid)
  1906. {
  1907. stateBlock.CopyToFrom(m_StateBuffers.m_PlayerStateBuffers.GetFrontBuffer(deviceIndex), defaultStateBuffer);
  1908. stateBlock.CopyToFrom(m_StateBuffers.m_PlayerStateBuffers.GetBackBuffer(deviceIndex), defaultStateBuffer);
  1909. }
  1910. #if UNITY_EDITOR
  1911. if (m_StateBuffers.m_EditorStateBuffers.valid)
  1912. {
  1913. stateBlock.CopyToFrom(m_StateBuffers.m_EditorStateBuffers.GetFrontBuffer(deviceIndex), defaultStateBuffer);
  1914. stateBlock.CopyToFrom(m_StateBuffers.m_EditorStateBuffers.GetBackBuffer(deviceIndex), defaultStateBuffer);
  1915. }
  1916. #endif
  1917. }
  1918. private unsafe void InitializeDeviceState(InputDevice device)
  1919. {
  1920. Debug.Assert(device != null, "Device must not be null");
  1921. Debug.Assert(device.added, "Device must have been added");
  1922. Debug.Assert(device.stateBlock.byteOffset != InputStateBlock.InvalidOffset, "Device state block offset is invalid");
  1923. Debug.Assert(device.stateBlock.byteOffset + device.stateBlock.alignedSizeInBytes <= m_StateBuffers.sizePerBuffer,
  1924. "Device state block is not contained in state buffer");
  1925. var controls = device.allControls;
  1926. var controlCount = controls.Count;
  1927. var resetMaskBuffer = m_StateBuffers.resetMaskBuffer;
  1928. var haveControlsWithDefaultState = device.hasControlsWithDefaultState;
  1929. // Assume that everything in the device is noise. This way we also catch memory regions
  1930. // that are not actually covered by a control and implicitly mark them as noise (e.g. the
  1931. // report ID in HID input reports).
  1932. //
  1933. // NOTE: Noise is indicated by *unset* bits so we don't have to do anything here to start
  1934. // with all-noise as we expect noise mask memory to be cleared on allocation.
  1935. var noiseMaskBuffer = m_StateBuffers.noiseMaskBuffer;
  1936. // We first toggle all bits *on* and then toggle bits for noisy and dontReset controls *off* individually.
  1937. // We do this instead of just leaving all bits *off* and then going through controls that aren't noisy/dontReset *on*.
  1938. // If we did the latter, we'd have the problem that a parent control such as TouchControl would toggle on bits for
  1939. // the entirety of its state block and thus cover the state of all its child controls.
  1940. MemoryHelpers.SetBitsInBuffer(noiseMaskBuffer, (int)device.stateBlock.byteOffset, 0, (int)device.stateBlock.sizeInBits, false);
  1941. MemoryHelpers.SetBitsInBuffer(resetMaskBuffer, (int)device.stateBlock.byteOffset, 0, (int)device.stateBlock.sizeInBits, true);
  1942. // Go through controls.
  1943. var defaultStateBuffer = m_StateBuffers.defaultStateBuffer;
  1944. for (var n = 0; n < controlCount; ++n)
  1945. {
  1946. var control = controls[n];
  1947. // Don't allow controls that hijack state from other controls to set independent noise or dontReset flags.
  1948. if (control.usesStateFromOtherControl)
  1949. continue;
  1950. if (!control.noisy || control.dontReset)
  1951. {
  1952. ref var stateBlock = ref control.m_StateBlock;
  1953. Debug.Assert(stateBlock.byteOffset != InputStateBlock.InvalidOffset, "Byte offset is invalid on control's state block");
  1954. Debug.Assert(stateBlock.bitOffset != InputStateBlock.InvalidOffset, "Bit offset is invalid on control's state block");
  1955. Debug.Assert(stateBlock.sizeInBits != InputStateBlock.InvalidOffset, "Size is invalid on control's state block");
  1956. Debug.Assert(stateBlock.byteOffset >= device.stateBlock.byteOffset, "Control's offset is located below device's offset");
  1957. Debug.Assert(stateBlock.byteOffset + stateBlock.alignedSizeInBytes <=
  1958. device.stateBlock.byteOffset + device.stateBlock.alignedSizeInBytes, "Control state block lies outside of state buffer");
  1959. // If control isn't noisy, toggle its bits *on* in the noise mask.
  1960. if (!control.noisy)
  1961. MemoryHelpers.SetBitsInBuffer(noiseMaskBuffer, (int)stateBlock.byteOffset, (int)stateBlock.bitOffset,
  1962. (int)stateBlock.sizeInBits, true);
  1963. // If control shouldn't be reset, toggle its bits *off* in the reset mask.
  1964. if (control.dontReset)
  1965. MemoryHelpers.SetBitsInBuffer(resetMaskBuffer, (int)stateBlock.byteOffset, (int)stateBlock.bitOffset,
  1966. (int)stateBlock.sizeInBits, false);
  1967. }
  1968. // If control has default state, write it into to the device's default state.
  1969. if (haveControlsWithDefaultState && control.hasDefaultState)
  1970. control.m_StateBlock.Write(defaultStateBuffer, control.m_DefaultState);
  1971. }
  1972. // Copy default state to all front and back buffers.
  1973. if (haveControlsWithDefaultState)
  1974. {
  1975. ref var deviceStateBlock = ref device.m_StateBlock;
  1976. var deviceIndex = device.m_DeviceIndex;
  1977. if (m_StateBuffers.m_PlayerStateBuffers.valid)
  1978. {
  1979. deviceStateBlock.CopyToFrom(m_StateBuffers.m_PlayerStateBuffers.GetFrontBuffer(deviceIndex), defaultStateBuffer);
  1980. deviceStateBlock.CopyToFrom(m_StateBuffers.m_PlayerStateBuffers.GetBackBuffer(deviceIndex), defaultStateBuffer);
  1981. }
  1982. #if UNITY_EDITOR
  1983. if (m_StateBuffers.m_EditorStateBuffers.valid)
  1984. {
  1985. deviceStateBlock.CopyToFrom(m_StateBuffers.m_EditorStateBuffers.GetFrontBuffer(deviceIndex), defaultStateBuffer);
  1986. deviceStateBlock.CopyToFrom(m_StateBuffers.m_EditorStateBuffers.GetBackBuffer(deviceIndex), defaultStateBuffer);
  1987. }
  1988. #endif
  1989. }
  1990. }
  1991. private void OnNativeDeviceDiscovered(int deviceId, string deviceDescriptor)
  1992. {
  1993. // Make sure we're not adding to m_AvailableDevices before we restored what we
  1994. // had before a domain reload.
  1995. RestoreDevicesAfterDomainReloadIfNecessary();
  1996. // See if we have a disconnected device we can revive.
  1997. // NOTE: We do this all the way up here as the first thing before we even parse the JSON descriptor so
  1998. // if we do have a device we can revive, we can do so without allocating any GC memory.
  1999. var device = TryMatchDisconnectedDevice(deviceDescriptor);
  2000. // Parse description, if need be.
  2001. var description = device?.description ?? InputDeviceDescription.FromJson(deviceDescriptor);
  2002. // Add it.
  2003. var markAsRemoved = false;
  2004. try
  2005. {
  2006. // If we have a restricted set of supported devices, first check if it's a device
  2007. // we should support.
  2008. if (m_Settings.supportedDevices.Count > 0)
  2009. {
  2010. var layout = device != null ? device.m_Layout : TryFindMatchingControlLayout(ref description, deviceId);
  2011. if (!IsDeviceLayoutMarkedAsSupportedInSettings(layout))
  2012. {
  2013. // Not supported. Ignore device. Still will get added to m_AvailableDevices
  2014. // list in finally clause below. If later the set of supported devices changes
  2015. // so that the device is now supported, ApplySettings() will pull it back out
  2016. // and create the device.
  2017. markAsRemoved = true;
  2018. return;
  2019. }
  2020. }
  2021. if (device != null)
  2022. {
  2023. // It's a device we pulled from the disconnected list. Update the device with the
  2024. // new ID, re-add it and notify that we've reconnected.
  2025. device.m_DeviceId = deviceId;
  2026. device.m_DeviceFlags |= InputDevice.DeviceFlags.Native;
  2027. device.m_DeviceFlags &= ~InputDevice.DeviceFlags.DisabledInFrontend;
  2028. device.m_DeviceFlags &= ~InputDevice.DeviceFlags.DisabledWhileInBackground;
  2029. device.m_DeviceFlags &= ~InputDevice.DeviceFlags.DisabledStateHasBeenQueriedFromRuntime;
  2030. AddDevice(device);
  2031. DelegateHelpers.InvokeCallbacksSafe(ref m_DeviceChangeListeners, device, InputDeviceChange.Reconnected,
  2032. "InputSystem.onDeviceChange");
  2033. }
  2034. else
  2035. {
  2036. // Go through normal machinery to try to create a new device.
  2037. AddDevice(description, throwIfNoLayoutFound: false, deviceId: deviceId,
  2038. deviceFlags: InputDevice.DeviceFlags.Native);
  2039. }
  2040. }
  2041. // We're catching exceptions very aggressively here. The reason is that we don't want
  2042. // exceptions thrown as a result of trying to create devices from device discoveries reported
  2043. // by native to break the system as a whole. Instead, we want to make the error visible but then
  2044. // go and work with whatever devices we *did* manage to create successfully.
  2045. catch (Exception exception)
  2046. {
  2047. Debug.LogError($"Could not create a device for '{description}' (exception: {exception})");
  2048. }
  2049. finally
  2050. {
  2051. // Remember it. Do this *after* the AddDevice() call above so that if there's
  2052. // a listener creating layouts on the fly we won't end up matching this device and
  2053. // create an InputDevice right away (which would then conflict with the one we
  2054. // create in AddDevice).
  2055. ArrayHelpers.AppendWithCapacity(ref m_AvailableDevices, ref m_AvailableDeviceCount,
  2056. new AvailableDevice
  2057. {
  2058. description = description,
  2059. deviceId = deviceId,
  2060. isNative = true,
  2061. isRemoved = markAsRemoved,
  2062. });
  2063. }
  2064. }
  2065. private InputDevice TryMatchDisconnectedDevice(string deviceDescriptor)
  2066. {
  2067. for (var i = 0; i < m_DisconnectedDevicesCount; ++i)
  2068. {
  2069. var device = m_DisconnectedDevices[i];
  2070. var description = device.description;
  2071. // We don't parse the full description but rather go property by property in order to not
  2072. // allocate GC memory if we can avoid it.
  2073. if (!InputDeviceDescription.ComparePropertyToDeviceDescriptor("interface", description.interfaceName, deviceDescriptor))
  2074. continue;
  2075. if (!InputDeviceDescription.ComparePropertyToDeviceDescriptor("product", description.product, deviceDescriptor))
  2076. continue;
  2077. if (!InputDeviceDescription.ComparePropertyToDeviceDescriptor("manufacturer", description.manufacturer, deviceDescriptor))
  2078. continue;
  2079. if (!InputDeviceDescription.ComparePropertyToDeviceDescriptor("type", description.deviceClass, deviceDescriptor))
  2080. continue;
  2081. if (!InputDeviceDescription.ComparePropertyToDeviceDescriptor("capabilities", description.capabilities, deviceDescriptor))
  2082. continue;
  2083. if (!InputDeviceDescription.ComparePropertyToDeviceDescriptor("serial", description.serial, deviceDescriptor))
  2084. continue;
  2085. ArrayHelpers.EraseAtWithCapacity(m_DisconnectedDevices, ref m_DisconnectedDevicesCount, i);
  2086. return device;
  2087. }
  2088. return null;
  2089. }
  2090. private void InstallBeforeUpdateHookIfNecessary()
  2091. {
  2092. if (m_NativeBeforeUpdateHooked || m_Runtime == null)
  2093. return;
  2094. m_Runtime.onBeforeUpdate = OnBeforeUpdate;
  2095. m_NativeBeforeUpdateHooked = true;
  2096. }
  2097. private void RestoreDevicesAfterDomainReloadIfNecessary()
  2098. {
  2099. #if UNITY_EDITOR
  2100. if (m_SavedDeviceStates != null)
  2101. RestoreDevicesAfterDomainReload();
  2102. #endif
  2103. }
  2104. #if UNITY_EDITOR
  2105. private void SyncAllDevicesWhenEditorIsActivated()
  2106. {
  2107. var isActive = m_Runtime.isEditorActive;
  2108. if (isActive == m_EditorIsActive)
  2109. return;
  2110. m_EditorIsActive = isActive;
  2111. if (m_EditorIsActive)
  2112. SyncAllDevices();
  2113. }
  2114. private void SyncAllDevices()
  2115. {
  2116. for (var i = 0; i < m_DevicesCount; ++i)
  2117. {
  2118. // When the editor comes back into focus, we actually do want resets to happen
  2119. // for devices that don't support syncs as they will likely have missed input while
  2120. // we were in the background.
  2121. if (!m_Devices[i].RequestSync())
  2122. ResetDevice(m_Devices[i], issueResetCommand: true);
  2123. }
  2124. }
  2125. internal void SyncAllDevicesAfterEnteringPlayMode()
  2126. {
  2127. // Because we ignore all events between exiting edit mode and entering play mode,
  2128. // that includes any potential device resets/syncs/etc,
  2129. // we need to resync all devices after we're in play mode proper.
  2130. ////TODO: this is a hacky workaround, implement a proper solution where events from sync/resets are not ignored.
  2131. SyncAllDevices();
  2132. }
  2133. #endif
  2134. private void WarnAboutDevicesFailingToRecreateAfterDomainReload()
  2135. {
  2136. // If we still have any saved device states, we have devices that we couldn't figure
  2137. // out how to recreate after a domain reload. Log a warning for each of them and
  2138. // let go of them.
  2139. #if UNITY_EDITOR
  2140. if (m_SavedDeviceStates == null)
  2141. return;
  2142. for (var i = 0; i < m_SavedDeviceStates.Length; ++i)
  2143. {
  2144. ref var state = ref m_SavedDeviceStates[i];
  2145. Debug.LogWarning($"Could not recreate device '{state.name}' with layout '{state.layout}' after domain reload");
  2146. }
  2147. // At this point, we throw the device states away and forget about
  2148. // what we had before the domain reload.
  2149. m_SavedDeviceStates = null;
  2150. #endif
  2151. }
  2152. private void OnBeforeUpdate(InputUpdateType updateType)
  2153. {
  2154. // Restore devices before checking update mask. See InputSystem.RunInitialUpdate().
  2155. RestoreDevicesAfterDomainReloadIfNecessary();
  2156. if ((updateType & m_UpdateMask) == 0)
  2157. return;
  2158. InputStateBuffers.SwitchTo(m_StateBuffers, updateType);
  2159. InputUpdate.OnBeforeUpdate(updateType);
  2160. // For devices that have state callbacks, tell them we're carrying state over
  2161. // into the next frame.
  2162. if (m_HaveDevicesWithStateCallbackReceivers && updateType != InputUpdateType.BeforeRender) ////REVIEW: before-render handling is probably wrong
  2163. {
  2164. for (var i = 0; i < m_DevicesCount; ++i)
  2165. {
  2166. var device = m_Devices[i];
  2167. if (!device.hasStateCallbacks)
  2168. continue;
  2169. // NOTE: We do *not* perform a buffer flip here as we do not want to change what is the
  2170. // current and what is the previous state when we carry state forward. Rather,
  2171. // OnBeforeUpdate, if it modifies state, it modifies the current state directly.
  2172. // Also, for the same reasons, we do not modify the dynamic/fixed update counts
  2173. // on the device. If an event comes in in the upcoming update, it should lead to
  2174. // a buffer flip.
  2175. ((IInputStateCallbackReceiver)device).OnNextUpdate();
  2176. }
  2177. }
  2178. DelegateHelpers.InvokeCallbacksSafe(ref m_BeforeUpdateListeners, "onBeforeUpdate");
  2179. }
  2180. /// <summary>
  2181. /// Apply the settings in <see cref="m_Settings"/>.
  2182. /// </summary>
  2183. internal void ApplySettings()
  2184. {
  2185. // Sync update mask.
  2186. var newUpdateMask = InputUpdateType.Editor;
  2187. if ((m_UpdateMask & InputUpdateType.BeforeRender) != 0)
  2188. {
  2189. // BeforeRender updates are enabled in response to devices needing BeforeRender updates
  2190. // so we always preserve this if set.
  2191. newUpdateMask |= InputUpdateType.BeforeRender;
  2192. }
  2193. if (m_Settings.updateMode == InputSettings.s_OldUnsupportedFixedAndDynamicUpdateSetting)
  2194. m_Settings.updateMode = InputSettings.UpdateMode.ProcessEventsInDynamicUpdate;
  2195. switch (m_Settings.updateMode)
  2196. {
  2197. case InputSettings.UpdateMode.ProcessEventsInDynamicUpdate:
  2198. newUpdateMask |= InputUpdateType.Dynamic;
  2199. break;
  2200. case InputSettings.UpdateMode.ProcessEventsInFixedUpdate:
  2201. newUpdateMask |= InputUpdateType.Fixed;
  2202. break;
  2203. case InputSettings.UpdateMode.ProcessEventsManually:
  2204. newUpdateMask |= InputUpdateType.Manual;
  2205. break;
  2206. default:
  2207. throw new NotSupportedException("Invalid input update mode: " + m_Settings.updateMode);
  2208. }
  2209. #if UNITY_EDITOR
  2210. // In the editor, we force editor updates to be on even if InputEditorUserSettings.lockInputToGameView is
  2211. // on as otherwise we'll end up accumulating events in edit mode without anyone flushing the
  2212. // queue out regularly.
  2213. newUpdateMask |= InputUpdateType.Editor;
  2214. #endif
  2215. updateMask = newUpdateMask;
  2216. ////TODO: optimize this so that we don't repeatedly recreate state if we add/remove multiple devices
  2217. //// (same goes for not resolving actions repeatedly)
  2218. // Check if there's any native device we aren't using ATM which now fits
  2219. // the set of supported devices.
  2220. AddAvailableDevicesThatAreNowRecognized();
  2221. // If the settings restrict the set of supported devices, demote any native
  2222. // device we currently have that doesn't fit the requirements.
  2223. if (settings.supportedDevices.Count > 0)
  2224. {
  2225. for (var i = 0; i < m_DevicesCount; ++i)
  2226. {
  2227. var device = m_Devices[i];
  2228. var layout = device.m_Layout;
  2229. // If it's not in m_AvailableDevices, we don't automatically remove it.
  2230. // Whatever has been added directly through AddDevice(), we keep and don't
  2231. // restrict by `supportDevices`.
  2232. var isInAvailableDevices = false;
  2233. for (var n = 0; n < m_AvailableDeviceCount; ++n)
  2234. {
  2235. if (m_AvailableDevices[n].deviceId == device.deviceId)
  2236. {
  2237. isInAvailableDevices = true;
  2238. break;
  2239. }
  2240. }
  2241. if (!isInAvailableDevices)
  2242. continue;
  2243. // If the device layout isn't supported according to the current settings,
  2244. // remove the device.
  2245. if (!IsDeviceLayoutMarkedAsSupportedInSettings(layout))
  2246. {
  2247. RemoveDevice(device, keepOnListOfAvailableDevices: true);
  2248. --i;
  2249. }
  2250. }
  2251. }
  2252. // Apply feature flags.
  2253. if (m_Settings.m_FeatureFlags != null)
  2254. {
  2255. #if UNITY_EDITOR
  2256. runPlayerUpdatesInEditMode = m_Settings.IsFeatureEnabled(InputFeatureNames.kRunPlayerUpdatesInEditMode);
  2257. #endif
  2258. if (m_Settings.IsFeatureEnabled(InputFeatureNames.kUseWindowsGamingInputBackend))
  2259. {
  2260. var command = UseWindowsGamingInputCommand.Create(true);
  2261. if (ExecuteGlobalCommand(ref command) < 0)
  2262. Debug.LogError($"Could not enable Windows.Gaming.Input");
  2263. }
  2264. }
  2265. // Cache some values.
  2266. Touchscreen.s_TapTime = settings.defaultTapTime;
  2267. Touchscreen.s_TapDelayTime = settings.multiTapDelayTime;
  2268. Touchscreen.s_TapRadiusSquared = settings.tapRadius * settings.tapRadius;
  2269. // Extra clamp here as we can't tell what we're getting from serialized data.
  2270. ButtonControl.s_GlobalDefaultButtonPressPoint = Mathf.Clamp(settings.defaultButtonPressPoint, ButtonControl.kMinButtonPressPoint, float.MaxValue);
  2271. ButtonControl.s_GlobalDefaultButtonReleaseThreshold = settings.buttonReleaseThreshold;
  2272. // Update devices control optimization
  2273. foreach (var device in devices)
  2274. device.SetOptimizedControlDataTypeRecursively();
  2275. // Invalidate control caches due to potential changes to processors or value readers
  2276. foreach (var device in devices)
  2277. device.MarkAsStaleRecursively();
  2278. // Let listeners know.
  2279. DelegateHelpers.InvokeCallbacksSafe(ref m_SettingsChangedListeners,
  2280. "InputSystem.onSettingsChange");
  2281. }
  2282. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  2283. internal void ApplyActions()
  2284. {
  2285. // Let listeners know.
  2286. DelegateHelpers.InvokeCallbacksSafe(ref m_ActionsChangedListeners, "InputSystem.onActionsChange");
  2287. }
  2288. #endif
  2289. internal unsafe long ExecuteGlobalCommand<TCommand>(ref TCommand command)
  2290. where TCommand : struct, IInputDeviceCommandInfo
  2291. {
  2292. var ptr = (InputDeviceCommand*)UnsafeUtility.AddressOf(ref command);
  2293. // device id is irrelevant as we route it based on fourcc internally
  2294. return InputRuntime.s_Instance.DeviceCommand(0, ptr);
  2295. }
  2296. internal void AddAvailableDevicesThatAreNowRecognized()
  2297. {
  2298. for (var i = 0; i < m_AvailableDeviceCount; ++i)
  2299. {
  2300. var id = m_AvailableDevices[i].deviceId;
  2301. if (TryGetDeviceById(id) != null)
  2302. continue;
  2303. var layout = TryFindMatchingControlLayout(ref m_AvailableDevices[i].description, id);
  2304. if (!IsDeviceLayoutMarkedAsSupportedInSettings(layout)) continue;
  2305. if (layout.IsEmpty())
  2306. {
  2307. // If it's a device coming from the runtime, disable it.
  2308. if (id != InputDevice.InvalidDeviceId)
  2309. {
  2310. var command = DisableDeviceCommand.Create();
  2311. m_Runtime.DeviceCommand(id, ref command);
  2312. }
  2313. continue;
  2314. }
  2315. try
  2316. {
  2317. AddDevice(m_AvailableDevices[i].description, layout, deviceId: id,
  2318. deviceFlags: m_AvailableDevices[i].isNative ? InputDevice.DeviceFlags.Native : 0);
  2319. }
  2320. catch (Exception)
  2321. {
  2322. // the user might have changed the layout of one device, but others in the system might still have
  2323. // layouts we can't make sense of. Just quietly swallow exceptions from those so as not to spam
  2324. // the user with information about devices unrelated to what was actually changed.
  2325. }
  2326. }
  2327. }
  2328. private bool ShouldRunDeviceInBackground(InputDevice device)
  2329. {
  2330. var runDeviceInBackground =
  2331. m_Settings.backgroundBehavior != InputSettings.BackgroundBehavior.ResetAndDisableAllDevices &&
  2332. device.canRunInBackground;
  2333. // In editor, we may override canRunInBackground depending on the gameViewFocus setting.
  2334. #if UNITY_EDITOR
  2335. if (runDeviceInBackground)
  2336. {
  2337. if (m_Settings.editorInputBehaviorInPlayMode == InputSettings.EditorInputBehaviorInPlayMode.AllDevicesRespectGameViewFocus)
  2338. runDeviceInBackground = false;
  2339. else if (m_Settings.editorInputBehaviorInPlayMode == InputSettings.EditorInputBehaviorInPlayMode.PointersAndKeyboardsRespectGameViewFocus)
  2340. runDeviceInBackground = !(device is Pointer || device is Keyboard);
  2341. }
  2342. #endif
  2343. return runDeviceInBackground;
  2344. }
  2345. internal void OnFocusChanged(bool focus)
  2346. {
  2347. #if UNITY_EDITOR
  2348. SyncAllDevicesWhenEditorIsActivated();
  2349. if (!m_Runtime.isInPlayMode)
  2350. {
  2351. m_HasFocus = focus;
  2352. return;
  2353. }
  2354. var gameViewFocus = m_Settings.editorInputBehaviorInPlayMode;
  2355. #endif
  2356. var runInBackground =
  2357. #if UNITY_EDITOR
  2358. // In the editor, the player loop will always be run even if the Game View does not have focus. This
  2359. // amounts to runInBackground being always true in the editor, regardless of what the setting in
  2360. // the Player Settings window is.
  2361. //
  2362. // If, however, "Game View Focus" is set to "Exactly As In Player", we force code here down the same
  2363. // path as in the player.
  2364. gameViewFocus != InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView || m_Runtime.runInBackground;
  2365. #else
  2366. m_Runtime.runInBackground;
  2367. #endif
  2368. var backgroundBehavior = m_Settings.backgroundBehavior;
  2369. if (backgroundBehavior == InputSettings.BackgroundBehavior.IgnoreFocus && runInBackground)
  2370. {
  2371. // If runInBackground is true, no device changes should happen, even when focus is gained. So early out.
  2372. // If runInBackground is false, we still want to sync devices when focus is gained. So we need to continue further.
  2373. m_HasFocus = focus;
  2374. return;
  2375. }
  2376. #if UNITY_EDITOR
  2377. // Set the current update type while we process the focus changes to make sure we
  2378. // feed into the right buffer. No need to do this in the player as it doesn't have
  2379. // the editor/player confusion.
  2380. m_CurrentUpdate = m_UpdateMask.GetUpdateTypeForPlayer();
  2381. #endif
  2382. if (!focus)
  2383. {
  2384. // We only react to loss of focus when we will keep running in the background. If not,
  2385. // we'll do nothing and just wait for focus to come back (where we then try to sync all devices).
  2386. if (runInBackground)
  2387. {
  2388. for (var i = 0; i < m_DevicesCount; ++i)
  2389. {
  2390. // Determine whether to run this device in the background.
  2391. var device = m_Devices[i];
  2392. if (!device.enabled || ShouldRunDeviceInBackground(device))
  2393. continue;
  2394. // Disable the device. This will also soft-reset it.
  2395. EnableOrDisableDevice(device, false, DeviceDisableScope.TemporaryWhilePlayerIsInBackground);
  2396. // In case we invoked a callback that messed with our device array, adjust our index.
  2397. var index = m_Devices.IndexOfReference(device, m_DevicesCount);
  2398. if (index == -1)
  2399. --i;
  2400. else
  2401. i = index;
  2402. }
  2403. }
  2404. }
  2405. else
  2406. {
  2407. // On focus gain, reenable and sync devices.
  2408. for (var i = 0; i < m_DevicesCount; ++i)
  2409. {
  2410. var device = m_Devices[i];
  2411. // Re-enable the device if we disabled it on focus loss. This will also issue a sync.
  2412. if (device.disabledWhileInBackground)
  2413. EnableOrDisableDevice(device, true, DeviceDisableScope.TemporaryWhilePlayerIsInBackground);
  2414. // Try to sync. If it fails and we didn't run in the background, perform
  2415. // a reset instead. This is to cope with backends that are unable to sync but
  2416. // may still retain state which now may be outdated because the input device may
  2417. // have changed state while we weren't running. So at least make the backend flush
  2418. // its state (if any).
  2419. else if (device.enabled && !runInBackground && !device.RequestSync())
  2420. ResetDevice(device);
  2421. }
  2422. }
  2423. #if UNITY_EDITOR
  2424. m_CurrentUpdate = InputUpdateType.None;
  2425. #endif
  2426. // We set this *after* the block above as defaultUpdateType is influenced by the setting.
  2427. m_HasFocus = focus;
  2428. }
  2429. #if UNITY_EDITOR
  2430. internal void LeavePlayMode()
  2431. {
  2432. // Reenable all devices and reset their play mode state.
  2433. m_CurrentUpdate = InputUpdate.GetUpdateTypeForPlayer(m_UpdateMask);
  2434. InputStateBuffers.SwitchTo(m_StateBuffers, m_CurrentUpdate);
  2435. for (var i = 0; i < m_DevicesCount; ++i)
  2436. {
  2437. var device = m_Devices[i];
  2438. if (device.disabledWhileInBackground)
  2439. EnableOrDisableDevice(device, true, scope: DeviceDisableScope.TemporaryWhilePlayerIsInBackground);
  2440. ResetDevice(device, alsoResetDontResetControls: true);
  2441. }
  2442. m_CurrentUpdate = default;
  2443. }
  2444. private void OnPlayerLoopInitialization()
  2445. {
  2446. if (!gameIsPlaying || // if game is not playing
  2447. !InputUpdate.s_LatestUpdateType.IsEditorUpdate() || // or last update was not editor update
  2448. !InputUpdate.s_LatestNonEditorUpdateType.IsPlayerUpdate()) // or update before that was not player update
  2449. return; // then no need to restore anything
  2450. InputUpdate.RestoreStateAfterEditorUpdate();
  2451. InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup = latestNonEditorTimeOffsetToRealtimeSinceStartup;
  2452. InputStateBuffers.SwitchTo(m_StateBuffers, InputUpdate.s_LatestUpdateType);
  2453. }
  2454. #endif
  2455. internal bool ShouldRunUpdate(InputUpdateType updateType)
  2456. {
  2457. // We perform a "null" update after domain reloads and on startup to get our devices
  2458. // in place before the runtime calls MonoBehaviour callbacks. See InputSystem.RunInitialUpdate().
  2459. if (updateType == InputUpdateType.None)
  2460. return true;
  2461. var mask = m_UpdateMask;
  2462. #if UNITY_EDITOR
  2463. // If the player isn't running, the only thing we run is editor updates, except if
  2464. // explicitly overriden via `runUpdatesInEditMode`.
  2465. // NOTE: This means that in edit mode (outside of play mode) we *never* switch to player
  2466. // input state. So, any script anywhere will see input state from the editor. If you
  2467. // have an [ExecuteInEditMode] MonoBehaviour and it polls the gamepad, for example,
  2468. // it will see gamepad inputs going to the editor and respond to them.
  2469. if (!gameIsPlaying && updateType != InputUpdateType.Editor && !runPlayerUpdatesInEditMode)
  2470. return false;
  2471. #endif
  2472. return (updateType & mask) != 0;
  2473. }
  2474. /// <summary>
  2475. /// Process input events.
  2476. /// </summary>
  2477. /// <param name="updateType"></param>
  2478. /// <param name="eventBuffer"></param>
  2479. /// <remarks>
  2480. /// This method is the core workhorse of the input system. It is called from <see cref="UnityEngineInternal.Input.NativeInputSystem"/>.
  2481. /// Usually this happens in response to the player loop running and triggering updates at set points. However,
  2482. /// updates can also be manually triggered through <see cref="InputSystem.Update"/>.
  2483. ///
  2484. /// The method receives the event buffer used internally by the runtime to collect events.
  2485. ///
  2486. /// Note that update types do *NOT* say what the events we receive are for. The update type only indicates
  2487. /// where in the Unity's application loop we got called from. Where the event data goes depends wholly on
  2488. /// which buffers we activate in the update and write the event data into.
  2489. /// </remarks>
  2490. /// <exception cref="InvalidOperationException">Thrown if OnUpdate is called recursively.</exception>
  2491. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1809:AvoidExcessiveLocals", Justification = "TODO: Refactor later.")]
  2492. private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer eventBuffer)
  2493. {
  2494. ////TODO: switch from Profiler to CustomSampler API
  2495. // NOTE: This is *not* using try/finally as we've seen unreliability in the EndSample()
  2496. // execution (and we're not sure where it's coming from).
  2497. Profiler.BeginSample("InputUpdate");
  2498. if (m_InputEventStream.isOpen)
  2499. {
  2500. Profiler.EndSample();
  2501. throw new InvalidOperationException("Already have an event buffer set! Was OnUpdate() called recursively?");
  2502. }
  2503. // Restore devices before checking update mask. See InputSystem.RunInitialUpdate().
  2504. RestoreDevicesAfterDomainReloadIfNecessary();
  2505. // In the editor, we issue a sync on all devices when the editor comes back to the foreground.
  2506. #if UNITY_EDITOR
  2507. SyncAllDevicesWhenEditorIsActivated();
  2508. #endif
  2509. if ((updateType & m_UpdateMask) == 0)
  2510. {
  2511. Profiler.EndSample();
  2512. return;
  2513. }
  2514. WarnAboutDevicesFailingToRecreateAfterDomainReload();
  2515. // First update sends out startup analytics.
  2516. #if UNITY_ANALYTICS || UNITY_EDITOR
  2517. if (!m_HaveSentStartupAnalytics)
  2518. {
  2519. InputAnalytics.OnStartup(this);
  2520. m_HaveSentStartupAnalytics = true;
  2521. }
  2522. #endif
  2523. // Update metrics.
  2524. ++m_Metrics.totalUpdateCount;
  2525. #if UNITY_EDITOR
  2526. // If current update is editor update and previous update was non-editor,
  2527. // store the time offset so we can restore it right after editor update is complete
  2528. if (((updateType & InputUpdateType.Editor) == InputUpdateType.Editor) && (m_CurrentUpdate & InputUpdateType.Editor) == 0)
  2529. latestNonEditorTimeOffsetToRealtimeSinceStartup =
  2530. InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup;
  2531. #endif
  2532. // Store current time offset.
  2533. InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup = m_Runtime.currentTimeOffsetToRealtimeSinceStartup;
  2534. InputStateBuffers.SwitchTo(m_StateBuffers, updateType);
  2535. m_CurrentUpdate = updateType;
  2536. InputUpdate.OnUpdate(updateType);
  2537. // Ensure optimized controls are in valid state
  2538. foreach (var device in devices)
  2539. device.EnsureOptimizationTypeHasNotChanged();
  2540. var shouldProcessActionTimeouts = updateType.IsPlayerUpdate() && gameIsPlaying;
  2541. // See if we're supposed to only take events up to a certain time.
  2542. // NOTE: We do not require the events in the queue to be sorted. Instead, we will walk over
  2543. // all events in the buffer each time. Note that if there are multiple events for the same
  2544. // device, it depends on the producer of these events to queue them in correct order.
  2545. // Otherwise, once an event with a newer timestamp has been processed, events coming later
  2546. // in the buffer and having older timestamps will get rejected.
  2547. var currentTime = updateType == InputUpdateType.Fixed ? m_Runtime.currentTimeForFixedUpdate : m_Runtime.currentTime;
  2548. var timesliceEvents = (updateType == InputUpdateType.Fixed || updateType == InputUpdateType.BeforeRender) &&
  2549. InputSystem.settings.updateMode == InputSettings.UpdateMode.ProcessEventsInFixedUpdate;
  2550. // Figure out if we can just flush the buffer and early out.
  2551. var canFlushBuffer =
  2552. false
  2553. #if UNITY_EDITOR
  2554. // If out of focus and runInBackground is off and ExactlyAsInPlayer is on, discard input.
  2555. || (!gameHasFocus && m_Settings.editorInputBehaviorInPlayMode == InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView &&
  2556. (!m_Runtime.runInBackground ||
  2557. m_Settings.backgroundBehavior == InputSettings.BackgroundBehavior.ResetAndDisableAllDevices))
  2558. #else
  2559. || (!gameHasFocus && !m_Runtime.runInBackground)
  2560. #endif
  2561. ;
  2562. var canEarlyOut =
  2563. // Early out if there's no events to process.
  2564. eventBuffer.eventCount == 0
  2565. || canFlushBuffer
  2566. #if UNITY_EDITOR
  2567. // If we're in the background and not supposed to process events in this update (but somehow
  2568. // still ended up here), we're done.
  2569. || ((!gameHasFocus || gameShouldGetInputRegardlessOfFocus) &&
  2570. ((m_Settings.backgroundBehavior == InputSettings.BackgroundBehavior.ResetAndDisableAllDevices && updateType != InputUpdateType.Editor)
  2571. || (m_Settings.editorInputBehaviorInPlayMode == InputSettings.EditorInputBehaviorInPlayMode.AllDevicesRespectGameViewFocus && updateType != InputUpdateType.Editor)
  2572. || (m_Settings.backgroundBehavior == InputSettings.BackgroundBehavior.IgnoreFocus && m_Settings.editorInputBehaviorInPlayMode == InputSettings.EditorInputBehaviorInPlayMode.AllDeviceInputAlwaysGoesToGameView && updateType == InputUpdateType.Editor)
  2573. )
  2574. // When the game is playing and has focus, we never process input in editor updates. All we
  2575. // do is just switch to editor state buffers and then exit.
  2576. || (gameIsPlaying && gameHasFocus && updateType == InputUpdateType.Editor))
  2577. #endif
  2578. ;
  2579. #if UNITY_EDITOR
  2580. var dropStatusEvents = false;
  2581. if (!gameIsPlaying && gameShouldGetInputRegardlessOfFocus && (eventBuffer.sizeInBytes > (100 * 1024)))
  2582. {
  2583. // If the game is not playing but we're sending all input events to the game, the buffer can just grow unbounded.
  2584. // So, in that case, set a flag to say we'd like to drop status events, and do not early out.
  2585. canEarlyOut = false;
  2586. dropStatusEvents = true;
  2587. }
  2588. #endif
  2589. if (canEarlyOut)
  2590. {
  2591. // Normally, we process action timeouts after first processing all events. If we have no
  2592. // events, we still need to check timeouts.
  2593. if (shouldProcessActionTimeouts)
  2594. ProcessStateChangeMonitorTimeouts();
  2595. Profiler.EndSample();
  2596. InvokeAfterUpdateCallback(updateType);
  2597. if (canFlushBuffer)
  2598. eventBuffer.Reset();
  2599. m_CurrentUpdate = default;
  2600. return;
  2601. }
  2602. var processingStartTime = Stopwatch.GetTimestamp();
  2603. var totalEventLag = 0.0;
  2604. #if UNITY_EDITOR
  2605. var isPlaying = gameIsPlaying;
  2606. #endif
  2607. try
  2608. {
  2609. m_InputEventStream = new InputEventStream(ref eventBuffer, m_Settings.maxQueuedEventsPerUpdate);
  2610. var totalEventBytesProcessed = 0U;
  2611. InputEvent* skipEventMergingFor = null;
  2612. // Handle events.
  2613. while (m_InputEventStream.remainingEventCount > 0)
  2614. {
  2615. if (m_Settings.maxEventBytesPerUpdate > 0 &&
  2616. totalEventBytesProcessed >= m_Settings.maxEventBytesPerUpdate)
  2617. {
  2618. Debug.LogError(
  2619. "Exceeded budget for maximum input event throughput per InputSystem.Update(). Discarding remaining events. "
  2620. + "Increase InputSystem.settings.maxEventBytesPerUpdate or set it to 0 to remove the limit.");
  2621. break;
  2622. }
  2623. InputDevice device = null;
  2624. var currentEventReadPtr = m_InputEventStream.currentEventPtr;
  2625. Debug.Assert(!currentEventReadPtr->handled, "Event in buffer is already marked as handled");
  2626. // In before render updates, we only take state events and only those for devices
  2627. // that have before render updates enabled.
  2628. if (updateType == InputUpdateType.BeforeRender)
  2629. {
  2630. while (m_InputEventStream.remainingEventCount > 0)
  2631. {
  2632. Debug.Assert(!currentEventReadPtr->handled,
  2633. "Iterated to event in buffer that is already marked as handled");
  2634. device = TryGetDeviceById(currentEventReadPtr->deviceId);
  2635. if (device != null && device.updateBeforeRender &&
  2636. (currentEventReadPtr->type == StateEvent.Type ||
  2637. currentEventReadPtr->type == DeltaStateEvent.Type))
  2638. break;
  2639. currentEventReadPtr = m_InputEventStream.Advance(leaveEventInBuffer: true);
  2640. }
  2641. }
  2642. if (m_InputEventStream.remainingEventCount == 0)
  2643. break;
  2644. var currentEventTimeInternal = currentEventReadPtr->internalTime;
  2645. var currentEventType = currentEventReadPtr->type;
  2646. #if UNITY_EDITOR
  2647. if (dropStatusEvents)
  2648. {
  2649. // If the type here is a status event, ask advance not to leave the event in the buffer. Otherwise, leave it there.
  2650. if (currentEventType == StateEvent.Type || currentEventType == DeltaStateEvent.Type || currentEventType == IMECompositionEvent.Type)
  2651. m_InputEventStream.Advance(false);
  2652. else
  2653. m_InputEventStream.Advance(true);
  2654. continue;
  2655. }
  2656. #endif
  2657. // In the editor, we discard all input events that occur in-between exiting edit mode and having
  2658. // entered play mode as otherwise we'll spill a bunch of UI events that have occurred while the
  2659. // UI was sort of neither in this mode nor in that mode. This would usually lead to the game receiving
  2660. // an accumulation of spurious inputs right in one of its first updates.
  2661. //
  2662. // NOTE: There's a chance the solution here will prove inadequate on the long run. We may do things
  2663. // here such as throwing partial touches away and then letting the rest of a touch go through.
  2664. // Could be that ultimately we need to issue a full reset of all devices at the beginning of
  2665. // play mode in the editor.
  2666. #if UNITY_EDITOR
  2667. if ((currentEventType == StateEvent.Type ||
  2668. currentEventType == DeltaStateEvent.Type) &&
  2669. (updateType & InputUpdateType.Editor) == 0 &&
  2670. InputSystem.s_SystemObject.exitEditModeTime > 0 &&
  2671. currentEventTimeInternal >= InputSystem.s_SystemObject.exitEditModeTime &&
  2672. (currentEventTimeInternal < InputSystem.s_SystemObject.enterPlayModeTime ||
  2673. InputSystem.s_SystemObject.enterPlayModeTime == 0))
  2674. {
  2675. m_InputEventStream.Advance(false);
  2676. continue;
  2677. }
  2678. #endif
  2679. // If we're timeslicing, check if the event time is within limits.
  2680. if (timesliceEvents && currentEventTimeInternal >= currentTime)
  2681. {
  2682. m_InputEventStream.Advance(true);
  2683. continue;
  2684. }
  2685. // If we can't find the device, ignore the event.
  2686. if (device == null)
  2687. device = TryGetDeviceById(currentEventReadPtr->deviceId);
  2688. if (device == null)
  2689. {
  2690. #if UNITY_EDITOR
  2691. ////TODO: see if this is a device we haven't created and if so, just ignore
  2692. m_Diagnostics?.OnCannotFindDeviceForEvent(new InputEventPtr(currentEventReadPtr));
  2693. #endif
  2694. m_InputEventStream.Advance(false);
  2695. continue;
  2696. }
  2697. // In the editor, we may need to bump events from editor updates into player updates
  2698. // and vice versa.
  2699. #if UNITY_EDITOR
  2700. if (isPlaying && !gameHasFocus)
  2701. {
  2702. if (m_Settings.editorInputBehaviorInPlayMode == InputSettings.EditorInputBehaviorInPlayMode
  2703. .PointersAndKeyboardsRespectGameViewFocus &&
  2704. m_Settings.backgroundBehavior !=
  2705. InputSettings.BackgroundBehavior.ResetAndDisableAllDevices)
  2706. {
  2707. var isPointerOrKeyboard = device is Pointer || device is Keyboard;
  2708. if (updateType != InputUpdateType.Editor)
  2709. {
  2710. // Let everything but pointer and keyboard input through.
  2711. // If the event is from a pointer or keyboard, leave it in the buffer so it can be dealt with
  2712. // in a subsequent editor update. Otherwise, take it out.
  2713. if (isPointerOrKeyboard)
  2714. {
  2715. m_InputEventStream.Advance(true);
  2716. continue;
  2717. }
  2718. }
  2719. else
  2720. {
  2721. // Let only pointer and keyboard input through.
  2722. if (!isPointerOrKeyboard)
  2723. {
  2724. m_InputEventStream.Advance(true);
  2725. continue;
  2726. }
  2727. }
  2728. }
  2729. }
  2730. #endif
  2731. // If device is disabled, we let the event through only in certain cases.
  2732. // Removal and configuration change events should always be processed.
  2733. if (!device.enabled &&
  2734. currentEventType != DeviceRemoveEvent.Type &&
  2735. currentEventType != DeviceConfigurationEvent.Type &&
  2736. (device.m_DeviceFlags & (InputDevice.DeviceFlags.DisabledInRuntime |
  2737. InputDevice.DeviceFlags.DisabledWhileInBackground)) != 0)
  2738. {
  2739. #if UNITY_EDITOR
  2740. // If the device is disabled in the backend, getting events for them
  2741. // is something that indicates a problem in the backend so diagnose.
  2742. if ((device.m_DeviceFlags & InputDevice.DeviceFlags.DisabledInRuntime) != 0)
  2743. m_Diagnostics?.OnEventForDisabledDevice(currentEventReadPtr, device);
  2744. #endif
  2745. m_InputEventStream.Advance(false);
  2746. continue;
  2747. }
  2748. // Check if the device wants to merge successive events.
  2749. if (!settings.disableRedundantEventsMerging && device.hasEventMerger && currentEventReadPtr != skipEventMergingFor)
  2750. {
  2751. // NOTE: This relies on events in the buffer being consecutive for the same device. This is not
  2752. // necessarily the case for events coming in from the background event queue where parallel
  2753. // producers may create interleaved input sequences. This will be fixed once we have the
  2754. // new buffering scheme for input events working in the native runtime.
  2755. var nextEvent = m_InputEventStream.Peek();
  2756. // If there is next event after current one.
  2757. if ((nextEvent != null)
  2758. // And if next event is for the same device.
  2759. && (currentEventReadPtr->deviceId == nextEvent->deviceId)
  2760. // And if next event is in the same timeslicing slot.
  2761. && (timesliceEvents ? (nextEvent->internalTime < currentTime) : true)
  2762. )
  2763. {
  2764. // Then try to merge current event into next event.
  2765. if (((IEventMerger)device).MergeForward(currentEventReadPtr, nextEvent))
  2766. {
  2767. // And if succeeded, skip current event, as it was merged into next event.
  2768. m_InputEventStream.Advance(false);
  2769. continue;
  2770. }
  2771. // If we can't merge current event with next one for any reason, we assume the next event
  2772. // carries crucial entropy (button changed state, phase changed, counter changed, etc).
  2773. // Hence semantic meaning for current event is "can't merge current with next because next is different".
  2774. // But semantic meaning for next event is "next event carries important information and should be preserved",
  2775. // from that point of view next event should not be merged with current nor with _next after next_ event.
  2776. //
  2777. // For example, given such stream of events:
  2778. // Mouse Mouse Mouse Mouse Mouse Mouse Mouse
  2779. // Event no1 Event no2 Event no3 Event no4 Event no5 Event no6 Event no7
  2780. // Time 1 Time 2 Time 3 Time 4 Time 5 Time 6 Time 7
  2781. // Pos(10,20) Pos(12,21) Pos(13,23) Pos(14,24) Pos(16,25) Pos(17,27) Pos(18,28)
  2782. // Delta(1,1) Delta(2,1) Delta(1,2) Delta(1,1) Delta(2,1) Delta(1,2) Delta(1,1)
  2783. // BtnLeft(0) BtnLeft(0) BtnLeft(0) BtnLeft(1) BtnLeft(1) BtnLeft(1) BtnLeft(1)
  2784. //
  2785. // if we then merge without skipping next event here:
  2786. // Mouse Mouse
  2787. // Event no3 Event no7
  2788. // Time 3 Time 7
  2789. // Pos(13,23) Pos(18,28)
  2790. // Delta(4,4) Delta(5,5)
  2791. // BtnLeft(0) BtnLeft(1)
  2792. //
  2793. // As you can see, the event no4 containing mouse button press was lost,
  2794. // and with it we lose the important information of timestamp of mouse button press.
  2795. //
  2796. // With skipping merging next event we will get:
  2797. // Mouse Mouse Mouse
  2798. // Time 3 Time 4 Time 7
  2799. // Event no3 Event no4 Event no7
  2800. // Pos(13,23) Pos(14,24) Pos(18,28)
  2801. // Delta(3,3) Delta(1,1) Delta(4,4)
  2802. // BtnLeft(0) BtnLeft(1) BtnLeft(1)
  2803. //
  2804. // And no4 is preserved, with the exact timestamp of button press.
  2805. skipEventMergingFor = nextEvent;
  2806. }
  2807. }
  2808. // Give the device a chance to do something with data before we propagate it to event listeners.
  2809. if (device.hasEventPreProcessor)
  2810. {
  2811. #if UNITY_EDITOR
  2812. var eventSizeBeforePreProcessor = currentEventReadPtr->sizeInBytes;
  2813. #endif
  2814. var shouldProcess = ((IEventPreProcessor)device).PreProcessEvent(currentEventReadPtr);
  2815. #if UNITY_EDITOR
  2816. if (currentEventReadPtr->sizeInBytes > eventSizeBeforePreProcessor)
  2817. {
  2818. Profiler.EndSample();
  2819. throw new AccessViolationException($"'{device}'.PreProcessEvent tries to grow an event from {eventSizeBeforePreProcessor} bytes to {currentEventReadPtr->sizeInBytes} bytes, this will potentially corrupt events after the current event and/or cause out-of-bounds memory access.");
  2820. }
  2821. #endif
  2822. if (!shouldProcess)
  2823. {
  2824. // Skip event if PreProcessEvent considers it to be irrelevant.
  2825. m_InputEventStream.Advance(false);
  2826. continue;
  2827. }
  2828. }
  2829. // Give listeners a shot at the event.
  2830. // NOTE: We call listeners also for events where the device is disabled. This is crucial for code
  2831. // such as TouchSimulation that disables the originating devices and then uses its events to
  2832. // create simulated events from.
  2833. if (m_EventListeners.length > 0)
  2834. {
  2835. DelegateHelpers.InvokeCallbacksSafe(ref m_EventListeners,
  2836. new InputEventPtr(currentEventReadPtr), device, "InputSystem.onEvent");
  2837. // If a listener marks the event as handled, we don't process it further.
  2838. if (currentEventReadPtr->handled)
  2839. {
  2840. m_InputEventStream.Advance(false);
  2841. continue;
  2842. }
  2843. }
  2844. // Update metrics.
  2845. if (currentEventTimeInternal <= currentTime)
  2846. totalEventLag += currentTime - currentEventTimeInternal;
  2847. ++m_Metrics.totalEventCount;
  2848. m_Metrics.totalEventBytes += (int)currentEventReadPtr->sizeInBytes;
  2849. // Process.
  2850. switch (currentEventType)
  2851. {
  2852. case StateEvent.Type:
  2853. case DeltaStateEvent.Type:
  2854. var eventPtr = new InputEventPtr(currentEventReadPtr);
  2855. // Ignore the event if the last state update we received for the device was
  2856. // newer than this state event is. We don't allow devices to go back in time.
  2857. //
  2858. // NOTE: We make an exception here for devices that implement IInputStateCallbackReceiver (such
  2859. // as Touchscreen). For devices that dynamically incorporate state it can be hard ensuring
  2860. // a global ordering of events as there may be multiple substreams (e.g. each individual touch)
  2861. // that are generated in the backend and would require considerable work to ensure monotonically
  2862. // increasing timestamps across all such streams.
  2863. var deviceIsStateCallbackReceiver = device.hasStateCallbacks;
  2864. if (currentEventTimeInternal < device.m_LastUpdateTimeInternal &&
  2865. !(deviceIsStateCallbackReceiver && device.stateBlock.format != eventPtr.stateFormat))
  2866. {
  2867. #if UNITY_EDITOR
  2868. m_Diagnostics?.OnEventTimestampOutdated(new InputEventPtr(currentEventReadPtr), device);
  2869. #elif UNITY_ANDROID
  2870. // Android keyboards can send events out of order: Holding down a key will send multiple
  2871. // presses after a short time, like on most platforms. Unfortunately, on Android, the
  2872. // last of these "presses" can be timestamped to be after the event of the key release.
  2873. // If that happens, we'd skip the keyUp here, and the device state will have the key
  2874. // "stuck" pressed. So, special case here to not skip keyboard events on Android. ISXB-475
  2875. // N.B. Android seems to have similar issues with touch input (OnStateEvent, Touchscreen.cs)
  2876. if (!(device is Keyboard))
  2877. #endif
  2878. break;
  2879. }
  2880. // Update the state of the device from the event. If the device is an IInputStateCallbackReceiver,
  2881. // let the device handle the event. If not, we do it ourselves.
  2882. var haveChangedStateOtherThanNoise = true;
  2883. if (deviceIsStateCallbackReceiver)
  2884. {
  2885. m_ShouldMakeCurrentlyUpdatingDeviceCurrent = true;
  2886. // NOTE: We leave it to the device to make sure the event has the right format. This allows the
  2887. // device to handle multiple different incoming formats.
  2888. ((IInputStateCallbackReceiver)device).OnStateEvent(eventPtr);
  2889. haveChangedStateOtherThanNoise = m_ShouldMakeCurrentlyUpdatingDeviceCurrent;
  2890. }
  2891. else
  2892. {
  2893. // If the state format doesn't match, ignore the event.
  2894. if (device.stateBlock.format != eventPtr.stateFormat)
  2895. {
  2896. #if UNITY_EDITOR
  2897. m_Diagnostics?.OnEventFormatMismatch(currentEventReadPtr, device);
  2898. #endif
  2899. break;
  2900. }
  2901. haveChangedStateOtherThanNoise = UpdateState(device, eventPtr, updateType);
  2902. }
  2903. totalEventBytesProcessed += eventPtr.sizeInBytes;
  2904. // Update timestamp on device.
  2905. // NOTE: We do this here and not in UpdateState() so that InputState.Change() will *NOT* change timestamps.
  2906. // Only events should. If running play mode updates in editor, we want to defer to the play mode
  2907. // callbacks to set the last update time to avoid dropping events only processed by the editor state.
  2908. if (device.m_LastUpdateTimeInternal <= eventPtr.internalTime
  2909. #if UNITY_EDITOR
  2910. && !(updateType == InputUpdateType.Editor && runPlayerUpdatesInEditMode)
  2911. #endif
  2912. )
  2913. device.m_LastUpdateTimeInternal = eventPtr.internalTime;
  2914. // Make device current. Again, only do this when receiving events.
  2915. if (haveChangedStateOtherThanNoise)
  2916. device.MakeCurrent();
  2917. break;
  2918. case TextEvent.Type:
  2919. {
  2920. var textEventPtr = (TextEvent*)currentEventReadPtr;
  2921. if (device is ITextInputReceiver textInputReceiver)
  2922. {
  2923. var utf32Char = textEventPtr->character;
  2924. if (utf32Char >= 0x10000)
  2925. {
  2926. // Send surrogate pair.
  2927. utf32Char -= 0x10000;
  2928. var highSurrogate = 0xD800 + ((utf32Char >> 10) & 0x3FF);
  2929. var lowSurrogate = 0xDC00 + (utf32Char & 0x3FF);
  2930. textInputReceiver.OnTextInput((char)highSurrogate);
  2931. textInputReceiver.OnTextInput((char)lowSurrogate);
  2932. }
  2933. else
  2934. {
  2935. // Send single, plain character.
  2936. textInputReceiver.OnTextInput((char)utf32Char);
  2937. }
  2938. }
  2939. break;
  2940. }
  2941. case IMECompositionEvent.Type:
  2942. {
  2943. var imeEventPtr = (IMECompositionEvent*)currentEventReadPtr;
  2944. var textInputReceiver = device as ITextInputReceiver;
  2945. textInputReceiver?.OnIMECompositionChanged(imeEventPtr->compositionString);
  2946. break;
  2947. }
  2948. case DeviceRemoveEvent.Type:
  2949. {
  2950. RemoveDevice(device, keepOnListOfAvailableDevices: false);
  2951. // If it's a native device with a description, put it on the list of disconnected
  2952. // devices.
  2953. if (device.native && !device.description.empty)
  2954. {
  2955. ArrayHelpers.AppendWithCapacity(ref m_DisconnectedDevices,
  2956. ref m_DisconnectedDevicesCount, device);
  2957. DelegateHelpers.InvokeCallbacksSafe(ref m_DeviceChangeListeners,
  2958. device, InputDeviceChange.Disconnected, "InputSystem.onDeviceChange");
  2959. }
  2960. break;
  2961. }
  2962. case DeviceConfigurationEvent.Type:
  2963. device.NotifyConfigurationChanged();
  2964. InputActionState.OnDeviceChange(device, InputDeviceChange.ConfigurationChanged);
  2965. DelegateHelpers.InvokeCallbacksSafe(ref m_DeviceChangeListeners,
  2966. device, InputDeviceChange.ConfigurationChanged, "InputSystem.onDeviceChange");
  2967. break;
  2968. case DeviceResetEvent.Type:
  2969. ResetDevice(device,
  2970. alsoResetDontResetControls: ((DeviceResetEvent*)currentEventReadPtr)->hardReset);
  2971. break;
  2972. }
  2973. m_InputEventStream.Advance(leaveEventInBuffer: false);
  2974. }
  2975. m_Metrics.totalEventProcessingTime +=
  2976. ((double)(Stopwatch.GetTimestamp() - processingStartTime)) / Stopwatch.Frequency;
  2977. m_Metrics.totalEventLagTime += totalEventLag;
  2978. m_InputEventStream.Close(ref eventBuffer);
  2979. }
  2980. catch (Exception)
  2981. {
  2982. // We need to restore m_InputEventStream to a sound state
  2983. // to avoid failing recursive OnUpdate check next frame.
  2984. Profiler.EndSample();
  2985. m_InputEventStream.CleanUpAfterException();
  2986. throw;
  2987. }
  2988. if (shouldProcessActionTimeouts)
  2989. ProcessStateChangeMonitorTimeouts();
  2990. Profiler.EndSample();
  2991. ////FIXME: need to ensure that if someone calls QueueEvent() from an onAfterUpdate callback, we don't end up with a
  2992. //// mess in the event buffer
  2993. //// same goes for events that someone may queue from a change monitor callback
  2994. InvokeAfterUpdateCallback(updateType);
  2995. m_CurrentUpdate = default;
  2996. }
  2997. private void InvokeAfterUpdateCallback(InputUpdateType updateType)
  2998. {
  2999. // don't invoke the after update callback if this is an editor update and the game is playing. We
  3000. // skip event processing when playing in the editor and the game has focus, which means that any
  3001. // handlers for this delegate that query input state during this update will get no values.
  3002. if (updateType == InputUpdateType.Editor && gameIsPlaying)
  3003. return;
  3004. DelegateHelpers.InvokeCallbacksSafe(ref m_AfterUpdateListeners,
  3005. "InputSystem.onAfterUpdate");
  3006. }
  3007. private bool m_ShouldMakeCurrentlyUpdatingDeviceCurrent;
  3008. // This is a dirty hot fix to expose entropy from device back to input manager to make a choice if we want to make device current or not.
  3009. // A proper fix would be to change IInputStateCallbackReceiver.OnStateEvent to return bool to make device current or not.
  3010. internal void DontMakeCurrentlyUpdatingDeviceCurrent()
  3011. {
  3012. m_ShouldMakeCurrentlyUpdatingDeviceCurrent = false;
  3013. }
  3014. internal unsafe bool UpdateState(InputDevice device, InputEvent* eventPtr, InputUpdateType updateType)
  3015. {
  3016. Debug.Assert(eventPtr != null, "Received NULL event ptr");
  3017. var stateBlockOfDevice = device.m_StateBlock;
  3018. var stateBlockSizeOfDevice = stateBlockOfDevice.sizeInBits / 8; // Always byte-aligned; avoid calling alignedSizeInBytes.
  3019. var offsetInDeviceStateToCopyTo = 0u;
  3020. uint sizeOfStateToCopy;
  3021. uint receivedStateSize;
  3022. byte* ptrToReceivedState;
  3023. FourCC receivedStateFormat;
  3024. // Grab state data from event and decide where to copy to and how much to copy.
  3025. if (eventPtr->type == StateEvent.Type)
  3026. {
  3027. var stateEventPtr = (StateEvent*)eventPtr;
  3028. receivedStateFormat = stateEventPtr->stateFormat;
  3029. receivedStateSize = stateEventPtr->stateSizeInBytes;
  3030. ptrToReceivedState = (byte*)stateEventPtr->state;
  3031. // Ignore extra state at end of event.
  3032. sizeOfStateToCopy = receivedStateSize;
  3033. if (sizeOfStateToCopy > stateBlockSizeOfDevice)
  3034. sizeOfStateToCopy = stateBlockSizeOfDevice;
  3035. }
  3036. else
  3037. {
  3038. Debug.Assert(eventPtr->type == DeltaStateEvent.Type, "Given event must either be a StateEvent or a DeltaStateEvent");
  3039. var deltaEventPtr = (DeltaStateEvent*)eventPtr;
  3040. receivedStateFormat = deltaEventPtr->stateFormat;
  3041. receivedStateSize = deltaEventPtr->deltaStateSizeInBytes;
  3042. ptrToReceivedState = (byte*)deltaEventPtr->deltaState;
  3043. offsetInDeviceStateToCopyTo = deltaEventPtr->stateOffset;
  3044. // Ignore extra state at end of event.
  3045. sizeOfStateToCopy = receivedStateSize;
  3046. if (offsetInDeviceStateToCopyTo + sizeOfStateToCopy > stateBlockSizeOfDevice)
  3047. {
  3048. if (offsetInDeviceStateToCopyTo >= stateBlockSizeOfDevice)
  3049. return false; // Entire delta state is out of range.
  3050. sizeOfStateToCopy = stateBlockSizeOfDevice - offsetInDeviceStateToCopyTo;
  3051. }
  3052. }
  3053. Debug.Assert(device.m_StateBlock.format == receivedStateFormat, "Received state format does not match format of device");
  3054. // Write state.
  3055. return UpdateState(device, updateType, ptrToReceivedState, offsetInDeviceStateToCopyTo,
  3056. sizeOfStateToCopy, eventPtr->internalTime, eventPtr);
  3057. }
  3058. /// <summary>
  3059. /// This method is the workhorse for updating input state in the system. It runs all the logic of incorporating
  3060. /// new state into devices and triggering whatever change monitors are attached to the state memory that gets
  3061. /// touched.
  3062. /// </summary>
  3063. /// <remarks>
  3064. /// This method can be invoked from outside the event processing loop and the given data does not have to come
  3065. /// from an event.
  3066. ///
  3067. /// This method does NOT respect <see cref="IInputStateCallbackReceiver"/>. This means that the device will
  3068. /// NOT get a shot at intervening in the state write.
  3069. /// </remarks>
  3070. /// <param name="device">Device to update state on. <paramref name="stateOffsetInDevice"/> is relative to device's
  3071. /// starting offset in memory.</param>
  3072. /// <param name="eventPtr">Pointer to state event from which the state change was initiated. Null if the state
  3073. /// change is not coming from an event.</param>
  3074. internal unsafe bool UpdateState(InputDevice device, InputUpdateType updateType,
  3075. void* statePtr, uint stateOffsetInDevice, uint stateSize, double internalTime, InputEventPtr eventPtr = default)
  3076. {
  3077. var deviceIndex = device.m_DeviceIndex;
  3078. ref var stateBlockOfDevice = ref device.m_StateBlock;
  3079. ////TODO: limit stateSize and StateOffset by the device's state memory
  3080. var deviceBuffer = (byte*)InputStateBuffers.GetFrontBufferForDevice(deviceIndex);
  3081. // If state monitors need to be re-sorted, do it now.
  3082. // NOTE: This must happen with the monitors in non-signalled state!
  3083. SortStateChangeMonitorsIfNecessary(deviceIndex);
  3084. // Before we update state, let change monitors compare the old and the new state.
  3085. // We do this instead of first updating the front buffer and then comparing to the
  3086. // back buffer as that would require a buffer flip for each state change in order
  3087. // for the monitors to work reliably. By comparing the *event* data to the current
  3088. // state, we can have multiple state events in the same frame yet still get reliable
  3089. // change notifications.
  3090. var haveSignalledMonitors =
  3091. ProcessStateChangeMonitors(deviceIndex, statePtr,
  3092. deviceBuffer + stateBlockOfDevice.byteOffset,
  3093. stateSize, stateOffsetInDevice);
  3094. var deviceStateOffset = device.m_StateBlock.byteOffset + stateOffsetInDevice;
  3095. var deviceStatePtr = deviceBuffer + deviceStateOffset;
  3096. ////REVIEW: Should we do this only for events but not for InputState.Change()?
  3097. // If noise filtering on .current is turned on and the device may have noise,
  3098. // determine if the event carries signal or not.
  3099. var noiseMask = device.noisy
  3100. ? (byte*)InputStateBuffers.s_NoiseMaskBuffer + deviceStateOffset
  3101. : null;
  3102. // Compare the current state of the device to the newly received state but overlay
  3103. // the comparison by the noise mask.
  3104. var makeDeviceCurrent = !MemoryHelpers.MemCmpBitRegion(deviceStatePtr, statePtr,
  3105. 0, stateSize * 8, mask: noiseMask);
  3106. // Buffer flip.
  3107. var flipped = FlipBuffersForDeviceIfNecessary(device, updateType);
  3108. // Now write the state.
  3109. #if UNITY_EDITOR
  3110. if (updateType == InputUpdateType.Editor)
  3111. {
  3112. WriteStateChange(m_StateBuffers.m_EditorStateBuffers, deviceIndex, ref stateBlockOfDevice, stateOffsetInDevice,
  3113. statePtr, stateSize, flipped);
  3114. }
  3115. else
  3116. #endif
  3117. {
  3118. WriteStateChange(m_StateBuffers.m_PlayerStateBuffers, deviceIndex, ref stateBlockOfDevice,
  3119. stateOffsetInDevice, statePtr, stateSize, flipped);
  3120. }
  3121. // Notify listeners.
  3122. DelegateHelpers.InvokeCallbacksSafe(ref m_DeviceStateChangeListeners,
  3123. device, eventPtr, "InputSystem.onDeviceStateChange");
  3124. // Now that we've committed the new state to memory, if any of the change
  3125. // monitors fired, let the associated actions know.
  3126. if (haveSignalledMonitors)
  3127. FireStateChangeNotifications(deviceIndex, internalTime, eventPtr);
  3128. return makeDeviceCurrent;
  3129. }
  3130. private unsafe void WriteStateChange(InputStateBuffers.DoubleBuffers buffers, int deviceIndex,
  3131. ref InputStateBlock deviceStateBlock, uint stateOffsetInDevice, void* statePtr, uint stateSizeInBytes, bool flippedBuffers)
  3132. {
  3133. var frontBuffer = buffers.GetFrontBuffer(deviceIndex);
  3134. Debug.Assert(frontBuffer != null);
  3135. // If we're updating less than the full state, we need to preserve the parts we are not updating.
  3136. // Instead of trying to optimize here and only copy what we really need, we just go and copy the
  3137. // entire state of the device over.
  3138. //
  3139. // NOTE: This copying must only happen once, right after a buffer flip. Otherwise we may copy old,
  3140. // stale input state from the back buffer over state that has already been updated with newer
  3141. // data.
  3142. var deviceStateSize = deviceStateBlock.sizeInBits / 8; // Always byte-aligned; avoid calling alignedSizeInBytes.
  3143. if (flippedBuffers && deviceStateSize != stateSizeInBytes)
  3144. {
  3145. var backBuffer = buffers.GetBackBuffer(deviceIndex);
  3146. Debug.Assert(backBuffer != null);
  3147. UnsafeUtility.MemCpy(
  3148. (byte*)frontBuffer + deviceStateBlock.byteOffset,
  3149. (byte*)backBuffer + deviceStateBlock.byteOffset,
  3150. deviceStateSize);
  3151. }
  3152. if (InputSettings.readValueCachingFeatureEnabled)
  3153. {
  3154. // if the buffers have just been flipped, and we're doing a full state update, then the state from the
  3155. // previous update is now in the back buffer, and we should be comparing to that when checking what
  3156. // controls have changed
  3157. var buffer = (byte*)frontBuffer;
  3158. if (flippedBuffers && deviceStateSize == stateSizeInBytes)
  3159. buffer = (byte*)buffers.GetBackBuffer(deviceIndex);
  3160. m_Devices[deviceIndex].WriteChangedControlStates(buffer + deviceStateBlock.byteOffset, statePtr,
  3161. stateSizeInBytes, stateOffsetInDevice);
  3162. }
  3163. UnsafeUtility.MemCpy((byte*)frontBuffer + deviceStateBlock.byteOffset + stateOffsetInDevice, statePtr,
  3164. stateSizeInBytes);
  3165. }
  3166. // Flip front and back buffer for device, if necessary. May flip buffers for more than just
  3167. // the given update type.
  3168. // Returns true if there was a buffer flip.
  3169. private bool FlipBuffersForDeviceIfNecessary(InputDevice device, InputUpdateType updateType)
  3170. {
  3171. if (updateType == InputUpdateType.BeforeRender)
  3172. {
  3173. ////REVIEW: I think this is wrong; if we haven't flipped in the current dynamic or fixed update, we should do so now
  3174. // We never flip buffers for before render. Instead, we already write
  3175. // into the front buffer.
  3176. return false;
  3177. }
  3178. #if UNITY_EDITOR
  3179. ////REVIEW: should this use the editor update ticks as quasi-frame-boundaries?
  3180. // Updates go to the editor only if the game isn't playing or does not have focus.
  3181. // Otherwise we fall through to the logic that flips for the *next* dynamic and
  3182. // fixed updates.
  3183. if (updateType == InputUpdateType.Editor)
  3184. {
  3185. ////REVIEW: This isn't right. The editor does have update ticks which constitute the equivalent of player frames.
  3186. // The editor doesn't really have a concept of frame-to-frame operation the
  3187. // same way the player does. So we simply flip buffers on a device whenever
  3188. // a new state event for it comes in.
  3189. m_StateBuffers.m_EditorStateBuffers.SwapBuffers(device.m_DeviceIndex);
  3190. return true;
  3191. }
  3192. #endif
  3193. // Flip buffers if we haven't already for this frame.
  3194. if (device.m_CurrentUpdateStepCount != InputUpdate.s_UpdateStepCount)
  3195. {
  3196. m_StateBuffers.m_PlayerStateBuffers.SwapBuffers(device.m_DeviceIndex);
  3197. device.m_CurrentUpdateStepCount = InputUpdate.s_UpdateStepCount;
  3198. return true;
  3199. }
  3200. return false;
  3201. }
  3202. // Domain reload survival logic. Also used for pushing and popping input system
  3203. // state for testing.
  3204. // Stuff everything that we want to survive a domain reload into
  3205. // a m_SerializedState.
  3206. #if UNITY_EDITOR || DEVELOPMENT_BUILD
  3207. [Serializable]
  3208. internal struct DeviceState
  3209. {
  3210. // Preserving InputDevices is somewhat tricky business. Serializing
  3211. // them in full would involve pretty nasty work. We have the restriction,
  3212. // however, that everything needs to be created from layouts (it partly
  3213. // exists for the sake of reload survivability), so we should be able to
  3214. // just go and recreate the device from the layout. This also has the
  3215. // advantage that if the layout changes between reloads, the change
  3216. // automatically takes effect.
  3217. public string name;
  3218. public string layout;
  3219. public string variants;
  3220. public string[] usages;
  3221. public int deviceId;
  3222. public int participantId;
  3223. public InputDevice.DeviceFlags flags;
  3224. public InputDeviceDescription description;
  3225. public void Restore(InputDevice device)
  3226. {
  3227. var usageCount = usages.LengthSafe();
  3228. for (var i = 0; i < usageCount; ++i)
  3229. device.AddDeviceUsage(new InternedString(usages[i]));
  3230. device.m_ParticipantId = participantId;
  3231. }
  3232. }
  3233. /// <summary>
  3234. /// State we take across domain reloads.
  3235. /// </summary>
  3236. /// <remarks>
  3237. /// Most of the state we re-recreate in-between reloads and do not store
  3238. /// in this structure. In particular, we do not preserve anything from
  3239. /// the various RegisterXXX().
  3240. ///
  3241. /// WARNING
  3242. ///
  3243. /// Making changes to serialized data format will likely to break upgrading projects from older versions.
  3244. /// That is until you restart the editor, then we recreate everything from clean state.
  3245. /// </remarks>
  3246. [Serializable]
  3247. internal struct SerializedState
  3248. {
  3249. public int layoutRegistrationVersion;
  3250. public float pollingFrequency;
  3251. public DeviceState[] devices;
  3252. public AvailableDevice[] availableDevices;
  3253. public InputStateBuffers buffers;
  3254. public InputUpdate.SerializedState updateState;
  3255. public InputUpdateType updateMask;
  3256. public InputMetrics metrics;
  3257. public InputSettings settings;
  3258. public InputActionAsset actions;
  3259. #if UNITY_ANALYTICS || UNITY_EDITOR
  3260. public bool haveSentStartupAnalytics;
  3261. #endif
  3262. }
  3263. internal SerializedState SaveState()
  3264. {
  3265. // Devices.
  3266. var deviceCount = m_DevicesCount;
  3267. var deviceArray = new DeviceState[deviceCount];
  3268. for (var i = 0; i < deviceCount; ++i)
  3269. {
  3270. var device = m_Devices[i];
  3271. string[] usages = null;
  3272. if (device.usages.Count > 0)
  3273. usages = device.usages.Select(x => x.ToString()).ToArray();
  3274. var deviceState = new DeviceState
  3275. {
  3276. name = device.name,
  3277. layout = device.layout,
  3278. variants = device.variants,
  3279. deviceId = device.deviceId,
  3280. participantId = device.m_ParticipantId,
  3281. usages = usages,
  3282. description = device.m_Description,
  3283. flags = device.m_DeviceFlags
  3284. };
  3285. deviceArray[i] = deviceState;
  3286. }
  3287. return new SerializedState
  3288. {
  3289. layoutRegistrationVersion = m_LayoutRegistrationVersion,
  3290. pollingFrequency = m_PollingFrequency,
  3291. devices = deviceArray,
  3292. availableDevices = m_AvailableDevices?.Take(m_AvailableDeviceCount).ToArray(),
  3293. buffers = m_StateBuffers,
  3294. updateState = InputUpdate.Save(),
  3295. updateMask = m_UpdateMask,
  3296. metrics = m_Metrics,
  3297. settings = m_Settings,
  3298. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  3299. actions = m_Actions,
  3300. #endif
  3301. #if UNITY_ANALYTICS || UNITY_EDITOR
  3302. haveSentStartupAnalytics = m_HaveSentStartupAnalytics,
  3303. #endif
  3304. };
  3305. }
  3306. internal void RestoreStateWithoutDevices(SerializedState state)
  3307. {
  3308. m_StateBuffers = state.buffers;
  3309. m_LayoutRegistrationVersion = state.layoutRegistrationVersion + 1;
  3310. updateMask = state.updateMask;
  3311. m_Metrics = state.metrics;
  3312. m_PollingFrequency = state.pollingFrequency;
  3313. if (m_Settings != null)
  3314. Object.DestroyImmediate(m_Settings);
  3315. m_Settings = state.settings;
  3316. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  3317. // Note that we just reassign actions and never destroy them since always mapped to persisted asset
  3318. // and hence ownership lies with ADB.
  3319. m_Actions = state.actions;
  3320. #endif
  3321. #if UNITY_ANALYTICS || UNITY_EDITOR
  3322. m_HaveSentStartupAnalytics = state.haveSentStartupAnalytics;
  3323. #endif
  3324. ////REVIEW: instead of accessing globals here, we could move this to when we re-create devices
  3325. // Update state.
  3326. InputUpdate.Restore(state.updateState);
  3327. }
  3328. // If these are set, we clear them out on the first input update.
  3329. internal DeviceState[] m_SavedDeviceStates;
  3330. internal AvailableDevice[] m_SavedAvailableDevices;
  3331. /// <summary>
  3332. /// Recreate devices based on the devices we had before a domain reload.
  3333. /// </summary>
  3334. /// <remarks>
  3335. /// Note that device indices may change between domain reloads.
  3336. ///
  3337. /// We recreate devices using the layout information as it exists now as opposed to
  3338. /// as it existed before the domain reload. This means we'll be picking up any changes that
  3339. /// have happened to layouts as part of the reload (including layouts having been removed
  3340. /// entirely).
  3341. /// </remarks>
  3342. internal void RestoreDevicesAfterDomainReload()
  3343. {
  3344. Profiler.BeginSample("InputManager.RestoreDevicesAfterDomainReload");
  3345. using (InputDeviceBuilder.Ref())
  3346. {
  3347. DeviceState[] retainedDeviceStates = null;
  3348. var deviceStates = m_SavedDeviceStates;
  3349. var deviceCount = m_SavedDeviceStates.LengthSafe();
  3350. m_SavedDeviceStates = null; // Prevent layout matcher registering themselves on the fly from picking anything off this list.
  3351. for (var i = 0; i < deviceCount; ++i)
  3352. {
  3353. ref var deviceState = ref deviceStates[i];
  3354. var device = TryGetDeviceById(deviceState.deviceId);
  3355. if (device != null)
  3356. continue;
  3357. var layout = TryFindMatchingControlLayout(ref deviceState.description,
  3358. deviceState.deviceId);
  3359. if (layout.IsEmpty())
  3360. {
  3361. var previousLayout = new InternedString(deviceState.layout);
  3362. if (m_Layouts.HasLayout(previousLayout))
  3363. layout = previousLayout;
  3364. }
  3365. if (layout.IsEmpty() || !RestoreDeviceFromSavedState(ref deviceState, layout))
  3366. ArrayHelpers.Append(ref retainedDeviceStates, deviceState);
  3367. }
  3368. // See if we can make sense of an available device now that we couldn't make sense of
  3369. // before. This can be the case if there's new layout information that wasn't available
  3370. // before.
  3371. if (m_SavedAvailableDevices != null)
  3372. {
  3373. m_AvailableDevices = m_SavedAvailableDevices;
  3374. m_AvailableDeviceCount = m_SavedAvailableDevices.LengthSafe();
  3375. for (var i = 0; i < m_AvailableDeviceCount; ++i)
  3376. {
  3377. var device = TryGetDeviceById(m_AvailableDevices[i].deviceId);
  3378. if (device != null)
  3379. continue;
  3380. if (m_AvailableDevices[i].isRemoved)
  3381. continue;
  3382. var layout = TryFindMatchingControlLayout(ref m_AvailableDevices[i].description,
  3383. m_AvailableDevices[i].deviceId);
  3384. if (!layout.IsEmpty())
  3385. {
  3386. try
  3387. {
  3388. AddDevice(layout, m_AvailableDevices[i].deviceId,
  3389. deviceDescription: m_AvailableDevices[i].description,
  3390. deviceFlags: m_AvailableDevices[i].isNative ? InputDevice.DeviceFlags.Native : 0);
  3391. }
  3392. catch (Exception)
  3393. {
  3394. // Just ignore. Simply means we still can't really turn the device into something useful.
  3395. }
  3396. }
  3397. }
  3398. }
  3399. // Done. Discard saved arrays.
  3400. m_SavedDeviceStates = retainedDeviceStates;
  3401. m_SavedAvailableDevices = null;
  3402. }
  3403. Profiler.EndSample();
  3404. }
  3405. // We have two general types of devices we need to care about when recreating devices
  3406. // after domain reloads:
  3407. //
  3408. // A) device with InputDeviceDescription
  3409. // B) device created directly from specific layout
  3410. //
  3411. // A) should go through the normal matching process whereas B) should get recreated with
  3412. // layout of same name (if still available).
  3413. //
  3414. // So we kick device recreation off from two points:
  3415. //
  3416. // 1) From RegisterControlLayoutMatcher to catch A)
  3417. // 2) From RegisterControlLayout to catch B)
  3418. //
  3419. // Additionally, we have the complication that a layout a device was using was something
  3420. // dynamically registered from onFindLayoutForDevice. We don't do anything special about that.
  3421. // The first full input update will flush out the list of saved device states and at that
  3422. // point, any onFindLayoutForDevice hooks simply have to be in place. If they are, devices
  3423. // will get recreated appropriately.
  3424. //
  3425. // It would be much simpler to recreate all devices as the first thing in the first full input
  3426. // update but that would mean that devices would become available only very late. They would
  3427. // not, for example, be available when MonoBehaviour.Start methods are invoked.
  3428. private bool RestoreDeviceFromSavedState(ref DeviceState deviceState, InternedString layout)
  3429. {
  3430. // We assign the same device IDs here to newly created devices that they had
  3431. // before the domain reload. This is safe as device ID allocation is under the
  3432. // control of the runtime and not expected to be affected by a domain reload.
  3433. InputDevice device;
  3434. try
  3435. {
  3436. device = AddDevice(layout,
  3437. deviceDescription: deviceState.description,
  3438. deviceId: deviceState.deviceId,
  3439. deviceName: deviceState.name,
  3440. deviceFlags: deviceState.flags,
  3441. variants: new InternedString(deviceState.variants));
  3442. }
  3443. catch (Exception exception)
  3444. {
  3445. Debug.LogError(
  3446. $"Could not recreate input device '{deviceState.description}' with layout '{deviceState.layout}' and variants '{deviceState.variants}' after domain reload");
  3447. Debug.LogException(exception);
  3448. return true; // Don't try again.
  3449. }
  3450. deviceState.Restore(device);
  3451. return true;
  3452. }
  3453. #endif // UNITY_EDITOR || DEVELOPMENT_BUILD
  3454. }
  3455. }