Açıklama Yok
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.

InputSystem.cs 199KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.CodeAnalysis;
  4. using System.Runtime.CompilerServices;
  5. using UnityEngine.InputSystem.Haptics;
  6. using Unity.Collections.LowLevel.Unsafe;
  7. using UnityEngine.InputSystem.Controls;
  8. using UnityEngine.InputSystem.Layouts;
  9. using UnityEngine.InputSystem.LowLevel;
  10. using UnityEngine.InputSystem.DualShock;
  11. using UnityEngine.InputSystem.EnhancedTouch;
  12. using UnityEngine.InputSystem.HID;
  13. using UnityEngine.InputSystem.Users;
  14. using UnityEngine.InputSystem.XInput;
  15. using UnityEngine.InputSystem.Utilities;
  16. using UnityEngine.Profiling;
  17. #if UNITY_EDITOR
  18. using UnityEditor;
  19. using UnityEngine.InputSystem.Editor;
  20. using UnityEditor.Networking.PlayerConnection;
  21. #else
  22. using System.Linq;
  23. using UnityEngine.Networking.PlayerConnection;
  24. #endif
  25. #if UNITY_EDITOR
  26. using CustomBindingPathValidator = System.Func<string, System.Action>;
  27. #endif
  28. ////TODO: allow aliasing processors etc
  29. ////REVIEW: rename all references to "frame" to refer to "update" instead (e.g. wasPressedThisUpdate)?
  30. ////TODO: add APIs to get to the state blocks (equivalent to what you currently get with e.g. InputSystem.devices[0].currentStatePtr)
  31. ////FIXME: modal dialogs (or anything that interrupts normal Unity operation) are likely a problem for the system as is; there's a good
  32. //// chance the event queue will just get swamped; should be only the background queue though so I guess once it fills up we
  33. //// simply start losing input but it won't grow infinitely
  34. ////REVIEW: make more APIs thread-safe?
  35. ////REVIEW: it'd be great to be able to set up monitors from control paths (independently of actions; or should we just use actions?)
  36. ////REVIEW: have InputSystem.onTextInput that's fired directly from the event processing loop?
  37. //// (and allow text input events that have no associated target device? this way we don't need a keyboard to get text input)
  38. ////REVIEW: split lower-level APIs (anything mentioning events and state) off into InputSystemLowLevel API to make this API more focused?
  39. ////TODO: release native allocations when exiting
  40. namespace UnityEngine.InputSystem
  41. {
  42. /// <summary>
  43. /// This is the central hub for the input system.
  44. /// </summary>
  45. /// <remarks>
  46. /// This class has the central APIs for working with the input system. You
  47. /// can manage devices available in the system (<see cref="AddDevice{TDevice}"/>,
  48. /// <see cref="devices"/>, <see cref="onDeviceChange"/> and related APIs) or extend
  49. /// the input system with custom functionality (<see cref="RegisterLayout{TLayout}"/>,
  50. /// <see cref="RegisterInteraction{T}"/>, <see cref="RegisterProcessor{T}"/>,
  51. /// <see cref="RegisterBindingComposite{T}"/>, and related APIs).
  52. ///
  53. /// To control haptics globally, you can use <see cref="PauseHaptics"/>, <see cref="ResumeHaptics"/>,
  54. /// and <see cref="ResetHaptics"/>.
  55. ///
  56. /// To enable and disable individual devices (such as <see cref="Sensor"/> devices),
  57. /// you can use <see cref="EnableDevice"/> and <see cref="DisableDevice"/>.
  58. ///
  59. /// The input system is initialized as part of Unity starting up. It is generally safe
  60. /// to call the APIs here from any of Unity's script callbacks.
  61. ///
  62. /// Note that, like most Unity APIs, most of the properties and methods in this API can only
  63. /// be called on the main thread. However, select APIs like <see cref="QueueEvent"/> can be
  64. /// called from threads. Where this is the case, it is stated in the documentation.
  65. /// </remarks>
  66. [SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces", Justification = "Options for namespaces are limited due to the legacy input class. Agreed on this as the least bad solution.")]
  67. #if UNITY_EDITOR
  68. [InitializeOnLoad]
  69. #endif
  70. public static partial class InputSystem
  71. {
  72. #region Layouts
  73. /// <summary>
  74. /// Event that is signalled when the layout setup in the system changes.
  75. /// </summary>
  76. /// <remarks>
  77. /// First parameter is the name of the layout that has changed and second parameter is the
  78. /// type of change that has occurred.
  79. ///
  80. /// <example>
  81. /// <code>
  82. /// InputSystem.onLayoutChange +=
  83. /// (name, change) =>
  84. /// {
  85. /// switch (change)
  86. /// {
  87. /// case InputControlLayoutChange.Added:
  88. /// Debug.Log($"New layout {name} has been added");
  89. /// break;
  90. /// case InputControlLayoutChange.Removed:
  91. /// Debug.Log($"Layout {name} has been removed");
  92. /// break;
  93. /// case InputControlLayoutChange.Replaced:
  94. /// Debug.Log($"Layout {name} has been updated");
  95. /// break;
  96. /// }
  97. /// }
  98. /// </code>
  99. /// </example>
  100. /// </remarks>
  101. /// <seealso cref="InputControlLayout"/>
  102. public static event Action<string, InputControlLayoutChange> onLayoutChange
  103. {
  104. add
  105. {
  106. lock (s_Manager)
  107. s_Manager.onLayoutChange += value;
  108. }
  109. remove
  110. {
  111. lock (s_Manager)
  112. s_Manager.onLayoutChange -= value;
  113. }
  114. }
  115. /// <summary>
  116. /// Register a control layout based on a type.
  117. /// </summary>
  118. /// <param name="type">Type to derive a control layout from. Must be derived from <see cref="InputControl"/>.</param>
  119. /// <param name="name">Name to use for the layout. If null or empty, the short name of the type (<c>Type.Name</c>) will be used.</param>
  120. /// <param name="matches">Optional device matcher. If this is supplied, the layout will automatically
  121. /// be instantiated for newly discovered devices that match the description.</param>
  122. /// <exception cref="ArgumentNullException"><paramref name="type"/> is <c>null</c>.</exception>
  123. /// <remarks>
  124. /// When the layout is instantiated, the system will reflect on all public fields and properties of the type
  125. /// which have a value type derived from <see cref="InputControl"/> or which are annotated with <see cref="InputControlAttribute"/>.
  126. ///
  127. /// The type can be annotated with <see cref="InputControlLayoutAttribute"/> for additional options
  128. /// but the attribute is not necessary for a type to be usable as a control layout. Note that if the type
  129. /// does have <see cref="InputControlLayoutAttribute"/> and has set <see cref="InputControlLayoutAttribute.stateType"/>,
  130. /// the system will <em>not</em> reflect on properties and fields in the type but do that on the given
  131. /// state type instead.
  132. ///
  133. /// <example>
  134. /// <code>
  135. /// // InputControlLayoutAttribute attribute is only necessary if you want
  136. /// // to override default behavior that occurs when registering your device
  137. /// // as a layout.
  138. /// // The most common use of InputControlLayoutAttribute is to direct the system
  139. /// // to a custom "state struct" through the `stateType` property. See below for details.
  140. /// [InputControlLayout(displayName = "My Device", stateType = typeof(MyDeviceState))]
  141. /// #if UNITY_EDITOR
  142. /// [InitializeOnLoad]
  143. /// #endif
  144. /// public class MyDevice : InputDevice
  145. /// {
  146. /// public ButtonControl button { get; private set; }
  147. /// public AxisControl axis { get; private set; }
  148. ///
  149. /// // Register the device.
  150. /// static MyDevice()
  151. /// {
  152. /// // In case you want instance of your device to automatically be created
  153. /// // when specific hardware is detected by the Unity runtime, you have to
  154. /// // add one or more "device matchers" (InputDeviceMatcher) for the layout.
  155. /// // These matchers are compared to an InputDeviceDescription received from
  156. /// // the Unity runtime when a device is connected. You can add them either
  157. /// // using InputSystem.RegisterLayoutMatcher() or by directly specifying a
  158. /// // matcher when registering the layout.
  159. /// InputSystem.RegisterLayout&lt;MyDevice&gt;(
  160. /// // For the sake of demonstration, let's assume your device is a HID
  161. /// // and you want to match by PID and VID.
  162. /// matches: new InputDeviceMatcher()
  163. /// .WithInterface("HID")
  164. /// .WithCapability("PID", 1234)
  165. /// .WithCapability("VID", 5678));
  166. /// }
  167. ///
  168. /// // This is only to trigger the static class constructor to automatically run
  169. /// // in the player.
  170. /// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
  171. /// private static void InitializeInPlayer() {}
  172. ///
  173. /// protected override void FinishSetup()
  174. /// {
  175. /// base.FinishSetup();
  176. /// button = GetChildControl&lt;ButtonControl&gt;("button");
  177. /// axis = GetChildControl&lt;AxisControl&gt;("axis");
  178. /// }
  179. /// }
  180. ///
  181. /// // A "state struct" describes the memory format used by a device. Each device can
  182. /// // receive and store memory in its custom format. InputControls are then connected
  183. /// // the individual pieces of memory and read out values from them.
  184. /// [StructLayout(LayoutKind.Explicit, Size = 32)]
  185. /// public struct MyDeviceState : IInputStateTypeInfo
  186. /// {
  187. /// // In the case of a HID (which we assume for the sake of this demonstration),
  188. /// // the format will be "HID". In practice, the format will depend on how your
  189. /// // particular device is connected and fed into the input system.
  190. /// // The format is a simple FourCC code that "tags" state memory blocks for the
  191. /// // device to give a base level of safety checks on memory operations.
  192. /// public FourCC format => return new FourCC('H', 'I', 'D');
  193. ///
  194. /// // InputControlAttributes on fields tell the input system to create controls
  195. /// // for the public fields found in the struct.
  196. ///
  197. /// // Assume a 16bit field of buttons. Create one button that is tied to
  198. /// // bit #3 (zero-based). Note that buttons do not need to be stored as bits.
  199. /// // They can also be stored as floats or shorts, for example.
  200. /// [InputControl(name = "button", layout = "Button", bit = 3)]
  201. /// public ushort buttons;
  202. ///
  203. /// // Create a floating-point axis. The name, if not supplied, is taken from
  204. /// // the field.
  205. /// [InputControl(layout = "Axis")]
  206. /// public short axis;
  207. /// }
  208. /// </code>
  209. /// </example>
  210. ///
  211. /// Note that if <paramref name="matches"/> is supplied, it will immediately be matched
  212. /// against the descriptions (<see cref="InputDeviceDescription"/>) of all available devices.
  213. /// If it matches any description where no layout matched before, a new device will immediately
  214. /// be created (except if suppressed by <see cref="InputSettings.supportedDevices"/>). If it
  215. /// matches a description better (see <see cref="InputDeviceMatcher.MatchPercentage"/>) than
  216. /// the currently used layout, the existing device will be a removed and a new device with
  217. /// the newly registered layout will be created.
  218. ///
  219. /// See <see cref="Controls.StickControl"/> or <see cref="Gamepad"/> for examples of layouts.
  220. /// </remarks>
  221. /// <seealso cref="InputControlLayout"/>
  222. public static void RegisterLayout(Type type, string name = null, InputDeviceMatcher? matches = null)
  223. {
  224. if (type == null)
  225. throw new ArgumentNullException(nameof(type));
  226. if (string.IsNullOrEmpty(name))
  227. name = type.Name;
  228. s_Manager.RegisterControlLayout(name, type);
  229. if (matches != null)
  230. s_Manager.RegisterControlLayoutMatcher(name, matches.Value);
  231. }
  232. /// <summary>
  233. /// Register a type as a control layout.
  234. /// </summary>
  235. /// <typeparam name="T">Type to derive a control layout from.</typeparam>
  236. /// <param name="name">Name to use for the layout. If null or empty, the short name of the type will be used.</param>
  237. /// <param name="matches">Optional device matcher. If this is supplied, the layout will automatically
  238. /// be instantiated for newly discovered devices that match the description.</param>
  239. /// <remarks>
  240. /// This method is equivalent to calling <see cref="RegisterLayout(Type,string,InputDeviceMatcher?)"/> with
  241. /// <c>typeof(T)</c>. See that method for details of the layout registration process.
  242. /// </remarks>
  243. /// <seealso cref="RegisterLayout(Type,string,InputDeviceMatcher?)"/>
  244. public static void RegisterLayout<T>(string name = null, InputDeviceMatcher? matches = null)
  245. where T : InputControl
  246. {
  247. RegisterLayout(typeof(T), name, matches);
  248. }
  249. /// <summary>
  250. /// Register a layout in JSON format.
  251. /// </summary>
  252. /// <param name="json">JSON data describing the layout.</param>
  253. /// <param name="name">Optional name of the layout. If null or empty, the name is taken from the "name"
  254. /// property of the JSON data. If it is supplied, it will override the "name" property if present. If neither
  255. /// is supplied, an <see cref="ArgumentException"/> is thrown.</param>
  256. /// <param name="matches">Optional device matcher. If this is supplied, the layout will automatically
  257. /// be instantiated for newly discovered devices that match the description.</param>
  258. /// <exception cref="ArgumentNullException"><paramref name="json"/> is null or empty.</exception>
  259. /// <exception cref="ArgumentException">No name has been supplied either through <paramref name="name"/>
  260. /// or the "name" JSON property.</exception>
  261. /// <remarks>
  262. /// The JSON format makes it possible to create new device and control layouts completely
  263. /// in data. They have to ultimately be based on a layout backed by a C# type, however (e.g.
  264. /// <see cref="Gamepad"/>).
  265. ///
  266. /// Note that most errors in layouts will only be detected when instantiated (i.e. when a device or control is
  267. /// being created from a layout). The JSON data will, however, be parsed once on registration to check for a
  268. /// device description in the layout. JSON format errors will thus be detected during registration.
  269. ///
  270. /// <example>
  271. /// <code>
  272. /// InputSystem.RegisterLayout(@"
  273. /// {
  274. /// ""name"" : ""MyDevice"",
  275. /// ""controls"" : [
  276. /// {
  277. /// ""name"" : ""myButton"",
  278. /// ""layout"" : ""Button""
  279. /// }
  280. /// ]
  281. /// }
  282. /// );
  283. /// </code>
  284. /// </example>
  285. /// </remarks>
  286. /// <seealso cref="RemoveLayout"/>
  287. public static void RegisterLayout(string json, string name = null, InputDeviceMatcher? matches = null)
  288. {
  289. s_Manager.RegisterControlLayout(json, name);
  290. if (matches != null)
  291. s_Manager.RegisterControlLayoutMatcher(name, matches.Value);
  292. }
  293. /// <summary>
  294. /// Register a layout that applies overrides to one or more other layouts.
  295. /// </summary>
  296. /// <param name="json">Layout in JSON format.</param>
  297. /// <param name="name">Optional name of the layout. If null or empty, the name is taken from the "name"
  298. /// property of the JSON data. If it is supplied, it will override the "name" property if present. If neither
  299. /// is supplied, an <see cref="ArgumentException"/> is thrown.</param>
  300. /// <remarks>
  301. /// Layout overrides are layout pieces that are applied on top of existing layouts.
  302. /// This can be used to modify any layout in the system non-destructively. The process works the
  303. /// same as extending an existing layout except that instead of creating a new layout
  304. /// by merging the derived layout and the base layout, the overrides are merged
  305. /// directly into the base layout.
  306. ///
  307. /// The layout merging logic used for overrides, is the same as the one used for
  308. /// derived layouts, i.e. <see cref="InputControlLayout.MergeLayout"/>.
  309. ///
  310. /// Layouts used as overrides look the same as normal layouts and have the same format.
  311. /// The only difference is that they are explicitly registered as overrides.
  312. ///
  313. /// Note that unlike "normal" layouts, layout overrides have the ability to extend
  314. /// multiple base layouts. The changes from the override will simply be merged into
  315. /// each of the layouts it extends. Use the <c>extendMultiple</c> rather than the
  316. /// <c>extend</c> property in JSON to give a list of base layouts instead of a single
  317. /// one.
  318. ///
  319. /// <example>
  320. /// <code>
  321. /// // Override default button press points on the gamepad triggers.
  322. /// InputSystem.RegisterLayoutOverride(@"
  323. /// {
  324. /// ""name"" : ""CustomTriggerPressPoints"",
  325. /// ""extend"" : ""Gamepad"",
  326. /// ""controls"" : [
  327. /// { ""name"" : ""leftTrigger"", ""parameters"" : ""pressPoint=0.25"" },
  328. /// { ""name"" : ""rightTrigger"", ""parameters"" : ""pressPoint=0.25"" }
  329. /// ]
  330. /// }
  331. /// ");
  332. /// </code>
  333. /// </example>
  334. /// </remarks>
  335. public static void RegisterLayoutOverride(string json, string name = null)
  336. {
  337. s_Manager.RegisterControlLayout(json, name, isOverride: true);
  338. }
  339. /// <summary>
  340. /// Add an additional device matcher to an existing layout.
  341. /// </summary>
  342. /// <param name="layoutName">Name of the device layout that should be instantiated if <paramref name="matcher"/>
  343. /// matches an <see cref="InputDeviceDescription"/> of a discovered device.</param>
  344. /// <param name="matcher">Specification to match against <see cref="InputDeviceDescription"/> instances.</param>
  345. /// <remarks>
  346. /// Each device layout can have zero or more matchers associated with it. If any one of the
  347. /// matchers matches a given <see cref="InputDeviceDescription"/> (see <see cref="InputDeviceMatcher.MatchPercentage"/>)
  348. /// better than any other matcher (for the same or any other layout), then the given layout
  349. /// will be used for the discovered device.
  350. ///
  351. /// Note that registering a matcher may immediately lead to devices being created or recreated.
  352. /// If <paramref name="matcher"/> matches any devices currently on the list of unsupported devices
  353. /// (see <see cref="GetUnsupportedDevices()"/>), new <see cref="InputDevice"/>s will be created
  354. /// using the layout called <paramref name="layoutName"/>. Also, if <paramref name="matcher"/>
  355. /// matches the description of a device better than the matcher (if any) for the device's currently
  356. /// used layout, the device will be recreated using the given layout.
  357. /// </remarks>
  358. /// <exception cref="ArgumentNullException"><paramref name="layoutName"/> is <c>null</c> or empty/</exception>
  359. /// <exception cref="ArgumentException"><paramref name="matcher"/> is empty (<see cref="InputDeviceMatcher.empty"/>).</exception>
  360. /// <seealso cref="RegisterLayout(Type,string,InputDeviceMatcher?)"/>
  361. /// <seealso cref="TryFindMatchingLayout"/>
  362. public static void RegisterLayoutMatcher(string layoutName, InputDeviceMatcher matcher)
  363. {
  364. s_Manager.RegisterControlLayoutMatcher(layoutName, matcher);
  365. }
  366. /// <summary>
  367. /// Add an additional device matcher to the layout registered for <typeparamref name="TDevice"/>.
  368. /// </summary>
  369. /// <param name="matcher">A device matcher.</param>
  370. /// <typeparam name="TDevice">Type that has been registered as a layout. See <see cref="RegisterLayout{T}"/>.</typeparam>
  371. /// <remarks>
  372. /// Calling this method is equivalent to calling <see cref="RegisterLayoutMatcher(string,InputDeviceMatcher)"/>
  373. /// with the name under which <typeparamref name="TDevice"/> has been registered.
  374. /// </remarks>
  375. /// <exception cref="ArgumentException"><paramref name="matcher"/> is empty (<see cref="InputDeviceMatcher.empty"/>)
  376. /// -or- <typeparamref name="TDevice"/> has not been registered as a layout.</exception>
  377. public static void RegisterLayoutMatcher<TDevice>(InputDeviceMatcher matcher)
  378. where TDevice : InputDevice
  379. {
  380. s_Manager.RegisterControlLayoutMatcher(typeof(TDevice), matcher);
  381. }
  382. /// <summary>
  383. /// Register a builder that delivers an <see cref="InputControlLayout"/> instance on demand.
  384. /// </summary>
  385. /// <param name="buildMethod">Method to invoke to generate a layout when the layout is chosen.
  386. /// Should not cache the layout but rather return a fresh instance every time.</param>
  387. /// <param name="name">Name under which to register the layout. If a layout with the same
  388. /// name is already registered, the call to this method will replace the existing layout.</param>
  389. /// <param name="baseLayout">Name of the layout that the layout returned from <paramref name="buildMethod"/>
  390. /// will be based on. The system needs to know this in advance in order to update devices
  391. /// correctly if layout registrations in the system are changed.</param>
  392. /// <param name="matches">Optional matcher for an <see cref="InputDeviceDescription"/>. If supplied,
  393. /// it is equivalent to calling <see cref="RegisterLayoutMatcher"/>.</param>
  394. /// <exception cref="ArgumentNullException"><paramref name="buildMethod"/> is <c>null</c> -or-
  395. /// <paramref name="name"/> is <c>null</c> or empty.</exception>
  396. /// <remarks>
  397. /// Layout builders are most useful for procedurally building device layouts from metadata
  398. /// supplied by external systems. A good example is <see cref="HID"/> where the "HID" standard
  399. /// includes a way for input devices to describe their various inputs and outputs in the form
  400. /// of a <see cref="UnityEngine.InputSystem.HID.HID.HIDDeviceDescriptor"/>. While not sufficient to build a perfectly robust
  401. /// <see cref="InputDevice"/>, these descriptions are usually enough to at least make the device
  402. /// work out-of-the-box to some extent.
  403. ///
  404. /// The builder method would usually use <see cref="InputControlLayout.Builder"/> to build the
  405. /// actual layout.
  406. ///
  407. /// <example>
  408. /// <code>
  409. /// InputSystem.RegisterLayoutBuilder(
  410. /// () =>
  411. /// {
  412. /// var builder = new InputControlLayout.Builder()
  413. /// .WithType&lt;MyDevice&gt;();
  414. /// builder.AddControl("button1").WithLayout("Button");
  415. /// return builder.Build();
  416. /// }, "MyCustomLayout"
  417. /// }
  418. /// </code>
  419. /// </example>
  420. ///
  421. /// Layout builders can be used in combination with <see cref="onFindLayoutForDevice"/> to
  422. /// build layouts dynamically for devices as they are connected to the system.
  423. ///
  424. /// Be aware that the same builder <em>must</em> not build different layouts. Each
  425. /// layout registered in the system is considered to be immutable for as long as it
  426. /// is registered. So, if a layout builder is registered under the name "Custom", for
  427. /// example, then every time the builder is invoked, it must return the same identical
  428. /// <see cref="InputControlLayout"/>.
  429. /// </remarks>
  430. /// <seealso cref="InputControlLayout.Builder"/>
  431. /// <seealso cref="InputSystem.onFindLayoutForDevice"/>
  432. public static void RegisterLayoutBuilder(Func<InputControlLayout> buildMethod, string name,
  433. string baseLayout = null, InputDeviceMatcher? matches = null)
  434. {
  435. if (buildMethod == null)
  436. throw new ArgumentNullException(nameof(buildMethod));
  437. if (string.IsNullOrEmpty(name))
  438. throw new ArgumentNullException(nameof(name));
  439. s_Manager.RegisterControlLayoutBuilder(buildMethod, name, baseLayout: baseLayout);
  440. if (matches != null)
  441. s_Manager.RegisterControlLayoutMatcher(name, matches.Value);
  442. }
  443. /// <summary>
  444. /// Register a "baked" version of a device layout.
  445. /// </summary>
  446. /// <typeparam name="TDevice">C# class that represents the precompiled version of the device layout that the
  447. /// class is derived from.</typeparam>
  448. /// <param name="metadata">Metadata automatically generated for the precompiled layout.</param>
  449. /// <remarks>
  450. /// This method is used to register device implementations for which their layout has been "baked" into
  451. /// a C# class. To generate such a class, right-click a device layout in the input debugger and select
  452. /// "Generate Precompiled Layout". This generates a C# file containing a class that represents the precompiled
  453. /// version of the device layout. The class can be registered using this method.
  454. ///
  455. /// Note that registering a precompiled layout will not implicitly register the "normal" version of the layout.
  456. /// In other words, <see cref="RegisterLayout{TDevice}"/> must be called before calling this method.
  457. ///
  458. /// <example>
  459. /// <code>
  460. /// // Register the non-precompiled, normal version of the layout.
  461. /// InputSystem.RegisterLayout&lt;MyDevice&gt;();
  462. ///
  463. /// // Register a precompiled version of the layout.
  464. /// InputSystem.RegisterPrecompiledLayout&lt;PrecompiledMyDevice&gt;(PrecompiledMyDevice.metadata);
  465. ///
  466. /// // This implicitly uses the precompiled version.
  467. /// InputSystem.AddDevice&lt;MyDevice&gt;();
  468. /// </code>
  469. /// </example>
  470. ///
  471. /// The main advantage of precompiled layouts is that instantiating them is many times faster than the default
  472. /// device creation path. By default, when creating an <see cref="InputDevice"/>, the system will have to load
  473. /// the <see cref="InputControlLayout"/> for the device as well as any layouts used directly or indirectly by
  474. /// that layout. This in itself is a slow process that generates GC heap garbage and uses .NET reflection (which
  475. /// itself may add additional permanent data to the GC heap). In addition, interpreting the layouts to construct
  476. /// an <see cref="InputDevice"/> and populate it with <see cref="InputControl"/> children is not a fast process.
  477. ///
  478. /// A precompiled layout, however, has all necessary construction steps "baked" into the generated code. It will
  479. /// not use reflection and will generally generate little to no GC heap garbage.
  480. ///
  481. /// A precompiled layout derives from the C# device class whose layout is "baked". If, for example, you generate
  482. /// a precompiled version for <see cref="Keyboard"/>, the resulting class will be derived from <see cref="Keyboard"/>.
  483. /// When registering the precompiled layout. If someone afterwards creates a <see cref="Keyboard"/>, the precompiled
  484. /// version will implicitly be instantiated and thus skips the default device creation path that will construct
  485. /// a <see cref="Keyboard"/> device from an <see cref="InputControlLayout"/> (it will thus not require the
  486. /// <see cref="Keyboard"/> layout or any other layout it depends on to be loaded).
  487. ///
  488. /// Note that when layout overrides (see <see cref="RegisterLayoutOverride"/>) or new versions of
  489. /// existing layouts are registered (e.g. if you replace the built-in "Button" layout by registering
  490. /// a new layout with that name), precompiled layouts affected by the change will automatically be
  491. /// <em>removed</em>. This causes the system to fall back to the default device creation path which can
  492. /// take runtime layout changes into account.
  493. /// </remarks>
  494. public static void RegisterPrecompiledLayout<TDevice>(string metadata)
  495. where TDevice : InputDevice, new()
  496. {
  497. s_Manager.RegisterPrecompiledLayout<TDevice>(metadata);
  498. }
  499. /// <summary>
  500. /// Remove an already registered layout from the system.
  501. /// </summary>
  502. /// <param name="name">Name of the layout to remove. Note that layout names are case-insensitive.</param>
  503. /// <remarks>
  504. /// Note that removing a layout also removes all devices that directly or indirectly
  505. /// use the layout.
  506. ///
  507. /// This method can be used to remove both control or device layouts.
  508. /// </remarks>
  509. public static void RemoveLayout(string name)
  510. {
  511. s_Manager.RemoveControlLayout(name);
  512. }
  513. /// <summary>
  514. /// Try to match a description for an input device to a layout.
  515. /// </summary>
  516. /// <param name="deviceDescription">Description of an input device.</param>
  517. /// <returns>Name of the layout that has been matched to the given description or null if no
  518. /// matching layout was found.</returns>
  519. /// <remarks>
  520. /// This method performs the same matching process that is invoked if a device is reported
  521. /// by the Unity runtime or using <see cref="AddDevice(InputDeviceDescription)"/>. The result
  522. /// depends on the matches (<see cref="InputDeviceMatcher"/>) registered for the device
  523. /// layout in the system.
  524. ///
  525. /// <example>
  526. /// <code>
  527. /// var layoutName = InputSystem.TryFindMatchingLayout(
  528. /// new InputDeviceDescription
  529. /// {
  530. /// interface = "XInput",
  531. /// product = "Xbox Wired Controller",
  532. /// manufacturer = "Microsoft"
  533. /// }
  534. /// );
  535. /// </code>
  536. /// </example>
  537. /// </remarks>
  538. /// <seealso cref="RegisterLayoutMatcher{TDevice}"/>
  539. /// <seealso cref="RegisterLayoutMatcher(string,InputDeviceMatcher)"/>
  540. public static string TryFindMatchingLayout(InputDeviceDescription deviceDescription)
  541. {
  542. return s_Manager.TryFindMatchingControlLayout(ref deviceDescription);
  543. }
  544. /// <summary>
  545. /// Return a list with the names of all layouts that have been registered.
  546. /// </summary>
  547. /// <returns>A list of layout names.</returns>
  548. /// <seealso cref="LoadLayout"/>
  549. /// <seealso cref="ListLayoutsBasedOn"/>
  550. /// <seealso cref="RegisterLayout(System.Type,string,Nullable{InputDeviceMatcher})"/>
  551. public static IEnumerable<string> ListLayouts()
  552. {
  553. return s_Manager.ListControlLayouts();
  554. }
  555. /// <summary>
  556. /// List all the layouts that are based on the given layout.
  557. /// </summary>
  558. /// <param name="baseLayout">Name of a registered layout.</param>
  559. /// <exception cref="ArgumentNullException"><paramref name="baseLayout"/> is <c>null</c> or empty.</exception>
  560. /// <returns>The names of all registered layouts based on <paramref name="baseLayout"/>.</returns>
  561. /// <remarks>
  562. /// The list will not include layout overrides (see <see cref="RegisterLayoutOverride"/>).
  563. ///
  564. /// <example>
  565. /// <code>
  566. /// // List all gamepad layouts in the system.
  567. /// Debug.Log(string.Join("\n", InputSystem.ListLayoutsBasedOn("Gamepad"));
  568. /// </code>
  569. /// </example>
  570. /// </remarks>
  571. public static IEnumerable<string> ListLayoutsBasedOn(string baseLayout)
  572. {
  573. if (string.IsNullOrEmpty(baseLayout))
  574. throw new ArgumentNullException(nameof(baseLayout));
  575. return s_Manager.ListControlLayouts(basedOn: baseLayout);
  576. }
  577. ////TODO: allow loading an *unmerged* layout
  578. /// <summary>
  579. /// Load a registered layout.
  580. /// </summary>
  581. /// <param name="name">Name of the layout to load. Note that layout names are case-insensitive.</param>
  582. /// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c> or empty.</exception>
  583. /// <returns>The constructed layout instance or <c>null</c> if no layout of the given name could be found.</returns>
  584. /// <remarks>
  585. /// The result of this method is what's called a "fully merged" layout, i.e. a layout with
  586. /// the information of all the base layouts as well as from all overrides merged into it. See
  587. /// <see cref="InputControlLayout.MergeLayout"/> for details.
  588. ///
  589. /// What this means in practice is that all inherited controls and settings will be present
  590. /// on the layout.
  591. ///
  592. /// <example>
  593. /// // List all controls defined for gamepads.
  594. /// var gamepadLayout = InputSystem.LoadLayout("Gamepad");
  595. /// foreach (var control in gamepadLayout.controls)
  596. /// {
  597. /// // There may be control elements that are not introducing new controls but rather
  598. /// // change settings on controls added indirectly by other layouts referenced from
  599. /// // Gamepad. These are not adding new controls so we skip them here.
  600. /// if (control.isModifyingExistingControl)
  601. /// continue;
  602. ///
  603. /// Debug.Log($"Control: {control.name} ({control.layout])");
  604. /// }
  605. /// </example>
  606. ///
  607. /// However, note that controls which are added from other layouts referenced by the loaded layout
  608. /// will not necessarily be visible on it (they will only if referenced by a <see cref="InputControlLayout.ControlItem"/>
  609. /// where <see cref="InputControlLayout.ControlItem.isModifyingExistingControl"/> is <c>true</c>).
  610. /// For example, let's assume we have the following layout which adds a device with a single stick.
  611. ///
  612. /// <example>
  613. /// <code>
  614. /// InputSystem.RegisterLayout(@"
  615. /// {
  616. /// ""name"" : ""DeviceWithStick"",
  617. /// ""controls"" : [
  618. /// { ""name"" : ""stick"", ""layout"" : ""Stick"" }
  619. /// ]
  620. /// }
  621. /// ");
  622. /// </code>
  623. /// </example>
  624. ///
  625. /// If we load this layout, the <c>"stick"</c> control will be visible on the layout but the
  626. /// X and Y (as well as up/down/left/right) controls added by the <c>"Stick"</c> layout will
  627. /// not be.
  628. /// </remarks>
  629. /// <seealso cref="RegisterLayout(Type,string,Nullable{InputDeviceMatcher})"/>
  630. public static InputControlLayout LoadLayout(string name)
  631. {
  632. if (string.IsNullOrEmpty(name))
  633. throw new ArgumentNullException(nameof(name));
  634. ////FIXME: this will intern the name even if the operation fails
  635. return s_Manager.TryLoadControlLayout(new InternedString(name));
  636. }
  637. /// <summary>
  638. /// Load the layout registered for the given type.
  639. /// </summary>
  640. /// <typeparam name="TControl">An InputControl type.</typeparam>
  641. /// <returns>The layout registered for <typeparamref name="TControl"/> or <c>null</c> if no
  642. /// such layout exists.</returns>
  643. /// <remarks>
  644. /// This method is equivalent to calling <see cref="LoadLayout(string)"/> with the name
  645. /// of the layout under which <typeparamref name="TControl"/> has been registered.
  646. ///
  647. /// <example>
  648. /// <code>
  649. /// // Load the InputControlLayout generated from StickControl.
  650. /// var stickLayout = InputSystem.LoadLayout&lt;StickControl&gt;();
  651. /// </code>
  652. /// </example>
  653. /// </remarks>
  654. /// <seealso cref="LoadLayout(string)"/>
  655. public static InputControlLayout LoadLayout<TControl>()
  656. where TControl : InputControl
  657. {
  658. return s_Manager.TryLoadControlLayout(typeof(TControl));
  659. }
  660. /// <summary>
  661. /// Return the name of the layout that the layout registered as <paramref name="layoutName"/>
  662. /// is based on.
  663. /// </summary>
  664. /// <param name="layoutName">Name of a layout as registered with a method such as <see
  665. /// cref="RegisterLayout{T}(string,InputDeviceMatcher?)"/>. Case-insensitive.</param>
  666. /// <returns>Name of the immediate parent layout of <paramref name="layoutName"/> or <c>null</c> if no layout
  667. /// with the given name is registered or if it is not based on another layout or if it is a layout override.</returns>
  668. /// <exception cref="ArgumentNullException"><paramref name="layoutName"/> is <c>null</c> or empty.</exception>
  669. /// <remarks>
  670. /// This method does not work for layout overrides (which can be based on multiple base layouts). To find
  671. /// out which layouts a specific override registered with <see cref="RegisterLayoutOverride"/> is based on,
  672. /// load the layout with <see cref="LoadLayout"/> and inspect <see cref="InputControlLayout.baseLayouts"/>.
  673. /// This method will return <c>null</c> when <paramref name="layoutName"/> is the name of a layout override.
  674. ///
  675. /// One advantage of this method over calling <see cref="LoadLayout"/> and looking at <see cref="InputControlLayout.baseLayouts"/>
  676. /// is that this method does not have to actually load the layout but instead only performs a simple lookup.
  677. ///
  678. /// <example>
  679. /// <code>
  680. /// // Prints "Pointer".
  681. /// Debug.Log(InputSystem.GetNameOfBaseLayout("Mouse"));
  682. ///
  683. /// // Also works for control layouts. Prints "Axis".
  684. /// Debug.Log(InputSystem.GetNameOfBaseLayout("Button"));
  685. /// </code>
  686. /// </example>
  687. /// </remarks>
  688. /// <seealso cref="InputControlLayout.baseLayouts"/>
  689. public static string GetNameOfBaseLayout(string layoutName)
  690. {
  691. if (string.IsNullOrEmpty(layoutName))
  692. throw new ArgumentNullException(nameof(layoutName));
  693. var internedLayoutName = new InternedString(layoutName);
  694. if (InputControlLayout.s_Layouts.baseLayoutTable.TryGetValue(internedLayoutName, out var result))
  695. return result;
  696. return null;
  697. }
  698. /// <summary>
  699. /// Check whether the first layout is based on the second.
  700. /// </summary>
  701. /// <param name="firstLayoutName">Name of a registered <see cref="InputControlLayout"/>.</param>
  702. /// <param name="secondLayoutName">Name of a registered <see cref="InputControlLayout"/>.</param>
  703. /// <returns>True if <paramref name="firstLayoutName"/> is based on <paramref name="secondLayoutName"/>.</returns>
  704. /// <exception cref="ArgumentNullException"><paramref name="firstLayoutName"/> is <c>null</c> or empty -or-
  705. /// <paramref name="secondLayoutName"/> is <c>null</c> or empty.</exception>
  706. /// <remarks>
  707. /// This is
  708. /// <example>
  709. /// </example>
  710. /// </remarks>
  711. public static bool IsFirstLayoutBasedOnSecond(string firstLayoutName, string secondLayoutName)
  712. {
  713. if (string.IsNullOrEmpty(firstLayoutName))
  714. throw new ArgumentNullException(nameof(firstLayoutName));
  715. if (string.IsNullOrEmpty(secondLayoutName))
  716. throw new ArgumentNullException(nameof(secondLayoutName));
  717. var internedFirstName = new InternedString(firstLayoutName);
  718. var internedSecondName = new InternedString(secondLayoutName);
  719. if (internedFirstName == internedSecondName)
  720. return true;
  721. return InputControlLayout.s_Layouts.IsBasedOn(internedSecondName, internedFirstName);
  722. }
  723. #endregion
  724. #region Processors
  725. /// <summary>
  726. /// Register an <see cref="InputProcessor{TValue}"/> with the system.
  727. /// </summary>
  728. /// <param name="type">Type that implements <see cref="InputProcessor"/>.</param>
  729. /// <param name="name">Name to use for the processor. If <c>null</c> or empty, name will be taken from the short name
  730. /// of <paramref name="type"/> (if it ends in "Processor", that suffix will be clipped from the name). Names
  731. /// are case-insensitive.</param>
  732. /// <remarks>
  733. /// Processors are used by both bindings (see <see cref="InputBinding"/>) and by controls
  734. /// (see <see cref="InputControl"/>) to post-process input values as they are being requested
  735. /// from calls such as <see cref="InputAction.ReadValue{TValue}"/> or <see
  736. /// cref="InputControl{T}.ReadValue"/>.
  737. ///
  738. /// <example>
  739. /// <code>
  740. /// // Let's say that we want to define a processor that adds some random jitter to its input.
  741. /// // We have to pick a value type to operate on if we want to derive from InputProcessor&lt;T&gt;
  742. /// // so we go with float here.
  743. /// //
  744. /// // Also, as we will need to place our call to RegisterProcessor somewhere, we add attributes
  745. /// // to hook into Unity's initialization. This works differently in the editor and in the player,
  746. /// // so we use both [InitializeOnLoad] and [RuntimeInitializeOnLoadMethod].
  747. /// #if UNITY_EDITOR
  748. /// [InitializeOnLoad]
  749. /// #endif
  750. /// public class JitterProcessor : InputProcessor&lt;float&gt;
  751. /// {
  752. /// // Add a parameter that defines the amount of jitter we apply.
  753. /// // This will be editable in the Unity editor UI and can be set
  754. /// // programmatically in code. For example:
  755. /// //
  756. /// // myAction.AddBinding("&lt;Gamepad&gt;/rightTrigger",
  757. /// // processors: "jitter(amount=0.1)");
  758. /// //
  759. /// [Tooltip("Amount of jitter to apply. Will add a random value in the range [-amount..amount] "
  760. /// + "to each input value.)]
  761. /// public float amount;
  762. ///
  763. /// // Process is called when an input value is read from a control. This is
  764. /// // where we perform our jitter.
  765. /// public override float Process(float value, InputControl control)
  766. /// {
  767. /// return float + Random.Range(-amount, amount);
  768. /// }
  769. ///
  770. /// // [InitializeOnLoad] will call the static class constructor which
  771. /// // we use to call Register.
  772. /// #if UNITY_EDITOR
  773. /// static JitterProcessor()
  774. /// {
  775. /// Register();
  776. /// }
  777. /// #endif
  778. ///
  779. /// // [RuntimeInitializeOnLoadMethod] will make sure that Register gets called
  780. /// // in the player on startup.
  781. /// // NOTE: This will also get called when going into play mode in the editor. In that
  782. /// // case we get two calls to Register instead of one. We don't bother with that
  783. /// // here. Calling RegisterProcessor twice here doesn't do any harm.
  784. /// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
  785. /// static void Register()
  786. /// {
  787. /// // We don't supply a name here. The input system will take "JitterProcessor"
  788. /// // and automatically snip off the "Processor" suffix thus leaving us with
  789. /// // a name of "Jitter" (all this is case-insensitive).
  790. /// InputSystem.RegisterProcessor&lt;JitterProcessor&gt;();
  791. /// }
  792. /// }
  793. ///
  794. /// // It doesn't really make sense in our case as the default parameter editor is just
  795. /// // fine (it will pick up the tooltip we defined above) but let's say we want to replace
  796. /// // the default float edit field we get on the "amount" parameter with a slider. We can
  797. /// // do so by defining a custom parameter editor.
  798. /// //
  799. /// // NOTE: We don't need to have a registration call here. The input system will automatically
  800. /// // find our parameter editor based on the JitterProcessor type parameter we give to
  801. /// // InputParameterEditor&lt;T&gt;.
  802. /// #if UNITY_EDITOR
  803. /// public class JitterProcessorEditor : InputParameterEditor&lt;JitterProcessor&gt;
  804. /// {
  805. /// public override void OnGUI()
  806. /// {
  807. /// target.amount = EditorGUILayout.Slider(m_AmountLabel, target.amount, 0, 0.25f);
  808. /// }
  809. ///
  810. /// private GUIContent m_AmountLabel = new GUIContent("Amount",
  811. /// "Amount of jitter to apply. Will add a random value in the range [-amount..amount] "
  812. /// + "to each input value.);
  813. /// }
  814. /// #endif
  815. /// </code>
  816. /// </example>
  817. ///
  818. /// Note that it is allowed to register the same processor type multiple types with
  819. /// different names. When doing so, the first registration is considered as the "proper"
  820. /// name for the processor and all subsequent registrations will be considered aliases.
  821. ///
  822. /// See the <a href="../manual/Processors.html">manual</a> for more details.
  823. /// </remarks>
  824. /// <seealso cref="InputProcessor{T}"/>
  825. /// <seealso cref="InputBinding.processors"/>
  826. /// <seealso cref="InputAction.processors"/>
  827. /// <seealso cref="InputControlLayout.ControlItem.processors"/>
  828. /// <seealso cref="UnityEngine.InputSystem.Editor.InputParameterEditor{TObject}"/>
  829. public static void RegisterProcessor(Type type, string name = null)
  830. {
  831. if (type == null)
  832. throw new ArgumentNullException(nameof(type));
  833. // Default name to name of type without Processor suffix.
  834. if (string.IsNullOrEmpty(name))
  835. {
  836. name = type.Name;
  837. if (name.EndsWith("Processor"))
  838. name = name.Substring(0, name.Length - "Processor".Length);
  839. }
  840. // Flush out any precompiled layout depending on the processor.
  841. var precompiledLayouts = s_Manager.m_Layouts.precompiledLayouts;
  842. foreach (var key in new List<InternedString>(precompiledLayouts.Keys)) // Need to keep key list stable while iterating; ToList() for some reason not available with .NET Standard 2.0 on Mono.
  843. {
  844. if (StringHelpers.CharacterSeparatedListsHaveAtLeastOneCommonElement(precompiledLayouts[key].metadata, name, ';'))
  845. s_Manager.m_Layouts.precompiledLayouts.Remove(key);
  846. }
  847. s_Manager.processors.AddTypeRegistration(name, type);
  848. }
  849. /// <summary>
  850. /// Register an <see cref="InputProcessor{TValue}"/> with the system.
  851. /// </summary>
  852. /// <typeparam name="T">Type that implements <see cref="InputProcessor"/>.</typeparam>
  853. /// <param name="name">Name to use for the processor. If <c>null</c> or empty, name will be taken from the short name
  854. /// of <typeparamref name="T"/> (if it ends in "Processor", that suffix will be clipped from the name). Names
  855. /// are case-insensitive.</param>
  856. /// <remarks>
  857. /// Processors are used by both bindings (see <see cref="InputBinding"/>) and by controls
  858. /// (see <see cref="InputControl"/>) to post-process input values as they are being requested
  859. /// from calls such as <see cref="InputAction.ReadValue{TValue}"/> or <see
  860. /// cref="InputControl{T}.ReadValue"/>.
  861. ///
  862. /// <example>
  863. /// <code>
  864. /// // Let's say that we want to define a processor that adds some random jitter to its input.
  865. /// // We have to pick a value type to operate on if we want to derive from InputProcessor&lt;T&gt;
  866. /// // so we go with float here.
  867. /// //
  868. /// // Also, as we will need to place our call to RegisterProcessor somewhere, we add attributes
  869. /// // to hook into Unity's initialization. This works differently in the editor and in the player,
  870. /// // so we use both [InitializeOnLoad] and [RuntimeInitializeOnLoadMethod].
  871. /// #if UNITY_EDITOR
  872. /// [InitializeOnLoad]
  873. /// #endif
  874. /// public class JitterProcessor : InputProcessor&lt;float&gt;
  875. /// {
  876. /// // Add a parameter that defines the amount of jitter we apply.
  877. /// // This will be editable in the Unity editor UI and can be set
  878. /// // programmatically in code. For example:
  879. /// //
  880. /// // myAction.AddBinding("&lt;Gamepad&gt;/rightTrigger",
  881. /// // processors: "jitter(amount=0.1)");
  882. /// //
  883. /// [Tooltip("Amount of jitter to apply. Will add a random value in the range [-amount..amount] "
  884. /// + "to each input value.)]
  885. /// public float amount;
  886. ///
  887. /// // Process is called when an input value is read from a control. This is
  888. /// // where we perform our jitter.
  889. /// public override float Process(float value, InputControl control)
  890. /// {
  891. /// return float + Random.Range(-amount, amount);
  892. /// }
  893. ///
  894. /// // [InitializeOnLoad] will call the static class constructor which
  895. /// // we use to call Register.
  896. /// #if UNITY_EDITOR
  897. /// static JitterProcessor()
  898. /// {
  899. /// Register();
  900. /// }
  901. /// #endif
  902. ///
  903. /// // [RuntimeInitializeOnLoadMethod] will make sure that Register gets called
  904. /// // in the player on startup.
  905. /// // NOTE: This will also get called when going into play mode in the editor. In that
  906. /// // case we get two calls to Register instead of one. We don't bother with that
  907. /// // here. Calling RegisterProcessor twice here doesn't do any harm.
  908. /// [RuntimeInitializeOnLoadMethod]
  909. /// static void Register()
  910. /// {
  911. /// // We don't supply a name here. The input system will take "JitterProcessor"
  912. /// // and automatically snip off the "Processor" suffix thus leaving us with
  913. /// // a name of "Jitter" (all this is case-insensitive).
  914. /// InputSystem.RegisterProcessor&lt;JitterProcessor&gt;();
  915. /// }
  916. /// }
  917. ///
  918. /// // It doesn't really make sense in our case as the default parameter editor is just
  919. /// // fine (it will pick up the tooltip we defined above) but let's say we want to replace
  920. /// // the default float edit field we get on the "amount" parameter with a slider. We can
  921. /// // do so by defining a custom parameter editor.
  922. /// //
  923. /// // NOTE: We don't need to have a registration call here. The input system will automatically
  924. /// // find our parameter editor based on the JitterProcessor type parameter we give to
  925. /// // InputParameterEditor&lt;T&gt;.
  926. /// #if UNITY_EDITOR
  927. /// public class JitterProcessorEditor : InputParameterEditor&lt;JitterProcessor&gt;
  928. /// {
  929. /// public override void OnGUI()
  930. /// {
  931. /// target.amount = EditorGUILayout.Slider(m_AmountLabel, target.amount, 0, 0.25f);
  932. /// }
  933. ///
  934. /// private GUIContent m_AmountLabel = new GUIContent("Amount",
  935. /// "Amount of jitter to apply. Will add a random value in the range [-amount..amount] "
  936. /// + "to each input value.);
  937. /// }
  938. /// #endif
  939. /// </code>
  940. /// </example>
  941. ///
  942. /// Note that it is allowed to register the same processor type multiple types with
  943. /// different names. When doing so, the first registration is considered as the "proper"
  944. /// name for the processor and all subsequent registrations will be considered aliases.
  945. ///
  946. /// See the <a href="../manual/Processors.html">manual</a> for more details.
  947. /// </remarks>
  948. /// <seealso cref="InputProcessor{T}"/>
  949. /// <seealso cref="InputBinding.processors"/>
  950. /// <seealso cref="InputAction.processors"/>
  951. /// <seealso cref="InputControlLayout.ControlItem.processors"/>
  952. /// <seealso cref="UnityEngine.InputSystem.Editor.InputParameterEditor{TObject}"/>
  953. public static void RegisterProcessor<T>(string name = null)
  954. {
  955. RegisterProcessor(typeof(T), name);
  956. }
  957. /// <summary>
  958. /// Return the processor type registered under the given name. If no such processor
  959. /// has been registered, return <c>null</c>.
  960. /// </summary>
  961. /// <param name="name">Name of processor. Case-insensitive.</param>
  962. /// <exception cref="ArgumentNullException"><paramref name="name"/> is <c>null</c> or empty.</exception>
  963. /// <returns>The given processor type or <c>null</c> if not found.</returns>
  964. /// <seealso cref="RegisterProcessor{T}"/>
  965. public static Type TryGetProcessor(string name)
  966. {
  967. if (string.IsNullOrEmpty(name))
  968. throw new ArgumentNullException(nameof(name));
  969. return s_Manager.processors.LookupTypeRegistration(name);
  970. }
  971. /// <summary>
  972. /// List the names of all processors have been registered.
  973. /// </summary>
  974. /// <returns>List of registered processors.</returns>
  975. /// <remarks>
  976. /// Note that the result will include both "proper" names and aliases registered
  977. /// for processors. If, for example, a given type <c>JitterProcessor</c> has been registered
  978. /// under both "Jitter" and "Randomize", it will appear in the list with both those names.
  979. /// </remarks>
  980. /// <seealso cref="TryGetProcessor"/>
  981. /// <seealso cref="RegisterProcessor{T}"/>
  982. public static IEnumerable<string> ListProcessors()
  983. {
  984. return s_Manager.processors.names;
  985. }
  986. #endregion
  987. #region Devices
  988. /// <summary>
  989. /// The list of currently connected devices.
  990. /// </summary>
  991. /// <value>Currently connected devices.</value>
  992. /// <remarks>
  993. /// Note that accessing this property does not allocate. It gives read-only access
  994. /// directly to the system's internal array of devices.
  995. ///
  996. /// The value returned by this property should not be held on to. When the device
  997. /// setup in the system changes, any value previously returned by this property
  998. /// may become invalid. Query the property directly whenever you need it.
  999. /// </remarks>
  1000. /// <seealso cref="AddDevice{TDevice}"/>
  1001. /// <seealso cref="RemoveDevice"/>
  1002. public static ReadOnlyArray<InputDevice> devices => s_Manager.devices;
  1003. /// <summary>
  1004. /// Devices that have been disconnected but are retained by the input system in case
  1005. /// they are plugged back in.
  1006. /// </summary>
  1007. /// <value>Devices that have been retained by the input system in case they are plugged
  1008. /// back in.</value>
  1009. /// <remarks>
  1010. /// During gameplay it is undesirable to have the system allocate and release managed memory
  1011. /// as devices are unplugged and plugged back in as it would ultimately lead to GC spikes
  1012. /// during gameplay. To avoid that, input devices that have been reported by the <see cref="IInputRuntime">
  1013. /// runtime</see> and are removed through <see cref="DeviceRemoveEvent">events</see> are retained
  1014. /// by the system and then reused if the device is plugged back in.
  1015. ///
  1016. /// Note that the devices moved to disconnected status will still see a <see cref="InputDeviceChange.Removed"/>
  1017. /// notification and a <see cref="InputDeviceChange.Added"/> notification when plugged back in.
  1018. ///
  1019. /// To determine if a newly discovered device is one we have seen before, the system uses a
  1020. /// simple approach of comparing <see cref="InputDeviceDescription">device descriptions</see>.
  1021. /// Note that there can be errors and a device may be incorrectly classified as <see cref="InputDeviceChange.Reconnected"/>
  1022. /// when in fact it is a different device from before. The problem is that based on information
  1023. /// made available by platforms, it can be inherently difficult to determine whether a device is
  1024. /// indeed the very same one.
  1025. ///
  1026. /// For example, it is often not possible to determine with 100% certainty whether an identical looking device
  1027. /// to one we've previously seen on a different USB port is indeed the very same device. OSs will usually
  1028. /// reattach a USB device to its previous instance if it is plugged into the same USB port but create a
  1029. /// new instance of the same device is plugged into a different port.
  1030. ///
  1031. /// For devices that do relay their <see cref="InputDeviceDescription.serial">serials</see> the matching
  1032. /// is reliable.
  1033. ///
  1034. /// The list can be purged by calling <see cref="FlushDisconnectedDevices"/>. Doing so, will release
  1035. /// all reference we hold to the devices or any controls inside of them and allow the devices to be
  1036. /// reclaimed by the garbage collector.
  1037. ///
  1038. /// Note that if you call <see cref="RemoveDevice"/> explicitly, the given device is not retained
  1039. /// by the input system and will not appear on this list.
  1040. ///
  1041. /// Also note that devices on this list will be lost when domain reloads happen in the editor (i.e. on
  1042. /// script recompilation and when entering play mode).
  1043. /// </remarks>
  1044. /// <seealso cref="FlushDisconnectedDevices"/>
  1045. public static ReadOnlyArray<InputDevice> disconnectedDevices =>
  1046. new ReadOnlyArray<InputDevice>(s_Manager.m_DisconnectedDevices, 0,
  1047. s_Manager.m_DisconnectedDevicesCount);
  1048. /// <summary>
  1049. /// Event that is signalled when the device setup in the system changes.
  1050. /// </summary>
  1051. /// <value>Callback when device setup ni system changes.</value>
  1052. /// <remarks>
  1053. /// This can be used to detect when devices are added or removed as well as
  1054. /// detecting when existing devices change their configuration.
  1055. ///
  1056. /// <example>
  1057. /// <code>
  1058. /// InputSystem.onDeviceChange +=
  1059. /// (device, change) =>
  1060. /// {
  1061. /// switch (change)
  1062. /// {
  1063. /// case InputDeviceChange.Added:
  1064. /// Debug.Log("Device added: " + device);
  1065. /// break;
  1066. /// case InputDeviceChange.Removed:
  1067. /// Debug.Log("Device removed: " + device);
  1068. /// break;
  1069. /// case InputDeviceChange.ConfigurationChanged:
  1070. /// Debug.Log("Device configuration changed: " + device);
  1071. /// break;
  1072. /// }
  1073. /// };
  1074. /// </code>
  1075. /// </example>
  1076. /// </remarks>
  1077. /// <exception cref="ArgumentNullException">Delegate reference is <c>null</c>.</exception>
  1078. /// <seealso cref="devices"/>
  1079. /// <seealso cref="AddDevice{TDevice}"/>
  1080. /// <seealso cref="RemoveDevice"/>
  1081. public static event Action<InputDevice, InputDeviceChange> onDeviceChange
  1082. {
  1083. add
  1084. {
  1085. if (value == null)
  1086. throw new ArgumentNullException(nameof(value));
  1087. lock (s_Manager)
  1088. s_Manager.onDeviceChange += value;
  1089. }
  1090. remove
  1091. {
  1092. if (value == null)
  1093. throw new ArgumentNullException(nameof(value));
  1094. lock (s_Manager)
  1095. s_Manager.onDeviceChange -= value;
  1096. }
  1097. }
  1098. ////REVIEW: this one isn't really well-designed and the means of intercepting communication
  1099. //// with the backend should be revisited >1.0
  1100. /// <summary>
  1101. /// Event that is signalled when an <see cref="InputDeviceCommand"/> is sent to
  1102. /// an <see cref="InputDevice"/>.
  1103. /// </summary>
  1104. /// <value>Event that gets signalled on <see cref="InputDeviceCommand"/>s.</value>
  1105. /// <remarks>
  1106. /// This can be used to intercept commands and optionally handle them without them reaching
  1107. /// the <see cref="IInputRuntime"/>.
  1108. ///
  1109. /// The first delegate in the list that returns a result other than <c>null</c> is considered
  1110. /// to have handled the command. If a command is handled by a delegate in the list, it will
  1111. /// not be sent on to the runtime.
  1112. /// </remarks>
  1113. /// <exception cref="ArgumentNullException">Delegate reference is <c>null</c>.</exception>
  1114. /// <seealso cref="InputDevice.ExecuteCommand{TCommand}"/>
  1115. /// <seealso cref="IInputRuntime.DeviceCommand"/>
  1116. public static event InputDeviceCommandDelegate onDeviceCommand
  1117. {
  1118. add
  1119. {
  1120. if (value == null)
  1121. throw new ArgumentNullException(nameof(value));
  1122. lock (s_Manager)
  1123. s_Manager.onDeviceCommand += value;
  1124. }
  1125. remove
  1126. {
  1127. if (value == null)
  1128. throw new ArgumentNullException(nameof(value));
  1129. lock (s_Manager)
  1130. s_Manager.onDeviceCommand -= value;
  1131. }
  1132. }
  1133. /// <summary>
  1134. /// Event that is signalled when the system is trying to match a layout to
  1135. /// a device it has discovered.
  1136. /// </summary>
  1137. /// <remarks>
  1138. /// This event allows customizing the layout discovery process and to generate
  1139. /// layouts on the fly, if need be. When a device is reported from the Unity
  1140. /// runtime or through <see cref="AddDevice(InputDeviceDescription)"/>, it is
  1141. /// reported in the form of an <see cref="InputDeviceDescription"/>. The system
  1142. /// will take that description and run it through all the <see cref="InputDeviceMatcher"/>s
  1143. /// that have been registered for layouts (<see cref="RegisterLayoutMatcher{TDevice}"/>).
  1144. /// Based on that, it will come up with either no matching layout or with a single
  1145. /// layout that has the highest matching score according to <see
  1146. /// cref="InputDeviceMatcher.MatchPercentage"/> (or, in case multiple layouts have
  1147. /// the same score, the first one to achieve that score -- which is quasi-non-deterministic).
  1148. ///
  1149. /// It will then take this layout name (which, again, may be empty) and invoke this
  1150. /// event here passing it not only the layout name but also information such as the
  1151. /// <see cref="InputDeviceDescription"/> for the device. Each of the callbacks hooked
  1152. /// into the event will be run in turn. The <em>first</em> one to return a string
  1153. /// that is not <c>null</c> and not empty will cause a switch from the layout the
  1154. /// system has chosen to the layout that has been returned by the callback. The remaining
  1155. /// layouts after that will then be invoked with that newly selected name but will not
  1156. /// be able to change the name anymore.
  1157. ///
  1158. /// If none of the callbacks returns a string that is not <c>null</c> or empty,
  1159. /// the system will stick with the layout that it had initially selected.
  1160. ///
  1161. /// Once all callbacks have been run, the system will either have a final layout
  1162. /// name or not. If it does, a device is created using that layout. If it does not,
  1163. /// no device is created.
  1164. ///
  1165. /// One thing this allows is to generate callbacks on the fly. Let's say that if
  1166. /// an input device is reported with the "Custom" interface, we want to generate
  1167. /// a layout for it on the fly. For details about how to build layouts dynamically
  1168. /// from code, see <see cref="InputControlLayout.Builder"/> and <see cref="RegisterLayoutBuilder"/>.
  1169. ///
  1170. /// <example>
  1171. /// <code>
  1172. /// InputSystem.onFindLayoutForDevice +=
  1173. /// (deviceId, description, matchedLayout, runtime) =>
  1174. /// {
  1175. /// // If the system does have a matching layout, we do nothing.
  1176. /// // This could be the case, for example, if we already generated
  1177. /// // a layout for the device or if someone explicitly registered
  1178. /// // a layout.
  1179. /// if (!string.IsNullOrEmpty(matchedLayout))
  1180. /// return null; // Tell system we did nothing.
  1181. ///
  1182. /// // See if the reported device uses the "Custom" interface. We
  1183. /// // are only interested in those.
  1184. /// if (description.interfaceName != "Custom")
  1185. /// return null; // Tell system we did nothing.
  1186. ///
  1187. /// // So now we know that we want to build a layout on the fly
  1188. /// // for this device. What we do is to register what's called a
  1189. /// // layout builder. These can use C# code to build an InputControlLayout
  1190. /// // on the fly.
  1191. ///
  1192. /// // First we need to come up with a sufficiently unique name for the layout
  1193. /// // under which we register the builder. This will usually involve some
  1194. /// // information from the InputDeviceDescription we have been supplied with.
  1195. /// // Let's say we can sufficiently tell devices on our interface apart by
  1196. /// // product name alone. So we just do this:
  1197. /// var layoutName = "Custom" + description.product;
  1198. ///
  1199. /// // We also need an InputDeviceMatcher that in the future will automatically
  1200. /// // select our newly registered layout whenever a new device of the same type
  1201. /// // is connected. We can get one simply like so:
  1202. /// var matcher = InputDeviceMatcher.FromDescription(description);
  1203. ///
  1204. /// // With these pieces in place, we can register our builder which
  1205. /// // mainly consists of a delegate that will get invoked when an instance
  1206. /// // of InputControlLayout is needed for the layout.
  1207. /// InputSystem.RegisterLayoutBuilder(
  1208. /// () =>
  1209. /// {
  1210. /// // Here is where we do the actual building. In practice,
  1211. /// // this would probably look at the 'capabilities' property
  1212. /// // of the InputDeviceDescription we got and create a tailor-made
  1213. /// // layout. But what you put in the layout here really depends on
  1214. /// // the specific use case you have.
  1215. /// //
  1216. /// // We just add some preset things here which should still sufficiently
  1217. /// // serve as a demonstration.
  1218. /// //
  1219. /// // Note that we can base our layout here on whatever other layout
  1220. /// // in the system. We could extend Gamepad, for example. If we don't
  1221. /// // choose a base layout, the system automatically implies InputDevice.
  1222. ///
  1223. /// var builder = new InputControlLayout.Builder()
  1224. /// .WithDisplayName(description.product);
  1225. ///
  1226. /// // Add controls.
  1227. /// builder.AddControl("stick")
  1228. /// .WithLayout("Stick");
  1229. ///
  1230. /// return builder.Build();
  1231. /// },
  1232. /// layoutName,
  1233. /// matches: matcher);
  1234. ///
  1235. /// // So, we want the system to use our layout for the device that has just
  1236. /// // been connected. We return it from this callback to do that.
  1237. /// return layoutName;
  1238. /// };
  1239. /// </code>
  1240. /// </example>
  1241. ///
  1242. /// Note that it may appear like one could simply use <see cref="RegisterLayoutBuilder"/>
  1243. /// like below instead of going through <c>onFindLayoutForDevice</c>.
  1244. ///
  1245. /// <example>
  1246. /// <code>
  1247. /// InputSystem.RegisterLayoutBuilder(
  1248. /// () =>
  1249. /// {
  1250. /// // Layout building code from above...
  1251. /// },
  1252. /// "CustomLayout",
  1253. /// matches: new InputDeviceMatcher().WithInterface("Custom"));
  1254. /// </code>
  1255. /// </example>
  1256. ///
  1257. /// However, the difference here is that all devices using the "Custom" interface will
  1258. /// end up with the same single layout -- which has to be identical. By hooking into
  1259. /// <c>onFindLayoutForDevice</c>, it is possible to register a new layout for every new
  1260. /// type of device that is discovered and thus build a multitude of different layouts.
  1261. ///
  1262. /// It is best to register for this callback during startup. One way to do it is to
  1263. /// use <c>InitializeOnLoadAttribute</c> and <c>RuntimeInitializeOnLoadMethod</c>.
  1264. /// </remarks>
  1265. /// <seealso cref="RegisterLayoutBuilder"/>
  1266. /// <seealso cref="InputControlLayout"/>
  1267. public static event InputDeviceFindControlLayoutDelegate onFindLayoutForDevice
  1268. {
  1269. add
  1270. {
  1271. lock (s_Manager)
  1272. s_Manager.onFindControlLayoutForDevice += value;
  1273. }
  1274. remove
  1275. {
  1276. lock (s_Manager)
  1277. s_Manager.onFindControlLayoutForDevice -= value;
  1278. }
  1279. }
  1280. ////REVIEW: should this be disambiguated more to separate it more from sensor sampling frequency?
  1281. ////REVIEW: this should probably be exposed as an input setting
  1282. /// <summary>
  1283. /// Frequency at which devices that need polling are being queried in the background.
  1284. /// </summary>
  1285. /// <value>Polled device sampling frequency in Hertz.</value>
  1286. /// <remarks>
  1287. /// Input data is gathered from platform APIs either as events or polled periodically.
  1288. ///
  1289. /// In the former case, where we get input as events, the platform is responsible for monitoring
  1290. /// input devices and sending their state changes which the Unity runtime receives
  1291. /// and queues as <see cref="InputEvent"/>s. This form of input collection usually happens on a
  1292. /// system-specific thread (which may be Unity's main thread) as part of how the Unity player
  1293. /// loop operates. In most cases, this means that this form of input will invariably get picked up
  1294. /// once per frame.
  1295. ///
  1296. /// In the latter case, where input has to be explicitly polled from the system, the Unity runtime
  1297. /// will periodically sample the state of input devices and send it off as input events. Wherever
  1298. /// possible, this happens in the background at a fixed frequency on a dedicated thread. The
  1299. /// <c>pollingFrequency</c> property controls the rate at which this sampling happens.
  1300. ///
  1301. /// The unit is Hertz. A value of 120, for example, means that devices are sampled 120 times
  1302. /// per second.
  1303. ///
  1304. /// The default polling frequency is 60 Hz.
  1305. ///
  1306. /// For devices that are polled, the frequency setting will directly translate to changes in the
  1307. /// <see cref="InputEvent.time"/> patterns. At 60 Hz, for example, timestamps for a specific,
  1308. /// polled device will be spaced at roughly 1/60th of a second apart.
  1309. ///
  1310. /// Note that it depends on the platform which devices are polled (if any). On Win32, for example,
  1311. /// only XInput gamepads are polled.
  1312. ///
  1313. /// Also note that the polling frequency applies to all devices that are polled. It is not possible
  1314. /// to set polling frequency on a per-device basis.
  1315. /// </remarks>
  1316. public static float pollingFrequency
  1317. {
  1318. get => s_Manager.pollingFrequency;
  1319. set => s_Manager.pollingFrequency = value;
  1320. }
  1321. /// <summary>
  1322. /// Add a new device by instantiating the given device layout.
  1323. /// </summary>
  1324. /// <param name="layout">Name of the layout to instantiate. Must be a device layout. Note that
  1325. /// layout names are case-insensitive.</param>
  1326. /// <param name="name">Name to assign to the device. If null, the layout's display name (<see
  1327. /// cref="InputControlLayout.displayName"/> is used instead. Note that device names are made
  1328. /// unique automatically by the system by appending numbers to them (e.g. "gamepad", "gamepad1",
  1329. /// "gamepad2", etc.).</param>
  1330. /// <param name="variants">Semicolon-separated list of layout variants to use for the device.</param>
  1331. /// <exception cref="ArgumentNullException"><paramref name="layout"/> is <c>null</c> or empty.</exception>
  1332. /// <returns>The newly created input device.</returns>
  1333. /// <remarks>
  1334. /// The device will be added to the <see cref="devices"/> list and a notification on
  1335. /// <see cref="onDeviceChange"/> will be triggered.
  1336. ///
  1337. /// Note that adding a device to the system will allocate and also create garbage on the GC heap.
  1338. ///
  1339. /// <example>
  1340. /// <code>
  1341. /// // This is one way to instantiate the "Gamepad" layout.
  1342. /// InputSystem.AddDevice("Gamepad");
  1343. ///
  1344. /// // In this case, because the "Gamepad" layout is based on the Gamepad
  1345. /// // class, we can also do this instead:
  1346. /// InputSystem.AddDevice&lt;Gamepad&gt;();
  1347. /// </code>
  1348. /// </example>
  1349. /// </remarks>
  1350. /// <seealso cref="AddDevice{T}"/>
  1351. /// <seealso cref="RemoveDevice"/>
  1352. /// <seealso cref="onDeviceChange"/>
  1353. /// <seealso cref="InputDeviceChange.Added"/>
  1354. /// <seealso cref="devices"/>
  1355. /// <seealso cref="RegisterLayout(Type,string,Nullable{InputDeviceMatcher})"/>
  1356. public static InputDevice AddDevice(string layout, string name = null, string variants = null)
  1357. {
  1358. if (string.IsNullOrEmpty(layout))
  1359. throw new ArgumentNullException(nameof(layout));
  1360. return s_Manager.AddDevice(layout, name, new InternedString(variants));
  1361. }
  1362. /// <summary>
  1363. /// Add a new device by instantiating the layout registered for type <typeparamref name="TDevice"/>.
  1364. /// </summary>
  1365. /// <param name="name">Name to assign to the device. If null, the layout's display name (<see
  1366. /// cref="InputControlLayout.displayName"/> is used instead. Note that device names are made
  1367. /// unique automatically by the system by appending numbers to them (e.g. "gamepad", "gamepad1",
  1368. /// "gamepad2", etc.).</param>
  1369. /// <typeparam name="TDevice">Type of device to add.</typeparam>
  1370. /// <returns>The newly added device.</returns>
  1371. /// <exception cref="InvalidOperationException">Instantiating the layout for <typeparamref name="TDevice"/>
  1372. /// did not produce a device of type <typeparamref name="TDevice"/>.</exception>
  1373. /// <remarks>
  1374. /// The device will be added to the <see cref="devices"/> list and a notification on
  1375. /// <see cref="onDeviceChange"/> will be triggered.
  1376. ///
  1377. /// Note that adding a device to the system will allocate and also create garbage on the GC heap.
  1378. ///
  1379. /// <example>
  1380. /// <code>
  1381. /// // Add a gamepad.
  1382. /// InputSystem.AddDevice&lt;Gamepad&gt;();
  1383. /// </code>
  1384. /// </example>
  1385. /// </remarks>
  1386. /// <seealso cref="RemoveDevice"/>
  1387. /// <seealso cref="onDeviceChange"/>
  1388. /// <seealso cref="InputDeviceChange.Added"/>
  1389. /// <seealso cref="devices"/>
  1390. public static TDevice AddDevice<TDevice>(string name = null)
  1391. where TDevice : InputDevice
  1392. {
  1393. var device = s_Manager.AddDevice(typeof(TDevice), name);
  1394. if (!(device is TDevice deviceOfType))
  1395. {
  1396. // Consider the entire operation as failed, so remove the device we just added.
  1397. if (device != null)
  1398. RemoveDevice(device);
  1399. throw new InvalidOperationException(
  1400. $"Layout registered for type '{typeof(TDevice).Name}' did not produce a device of that type; layout probably has been overridden");
  1401. }
  1402. return deviceOfType;
  1403. }
  1404. /// <summary>
  1405. /// Tell the input system that a new device has become available.
  1406. /// </summary>
  1407. /// <param name="description">Description of the input device.</param>
  1408. /// <returns>The newly created device that has been added to <see cref="devices"/>.</returns>
  1409. /// <exception cref="ArgumentException">The given <paramref name="description"/> is empty -or-
  1410. /// no layout can be found that matches the given device <paramref name="description"/>.</exception>
  1411. /// <remarks>
  1412. /// This method is different from methods such as <see cref="AddDevice(string,string,string)"/>
  1413. /// or <see cref="AddDevice{TDevice}"/> in that it employs the usual matching process the
  1414. /// same way that it happens when the Unity runtime reports an input device.
  1415. ///
  1416. /// In particular, the same procedure described in the documentation for <see cref="onFindLayoutForDevice"/>
  1417. /// is employed where all registered <see cref="InputDeviceMatcher"/>s are matched against the
  1418. /// supplied device description and the most suitable match determines the layout to use. This in
  1419. /// turn is run through <see cref="onFindLayoutForDevice"/> to determine the final layout to use.
  1420. ///
  1421. /// If no suitable layout can be found, the method throws <c>ArgumentException</c>.
  1422. /// <example>
  1423. /// <code>
  1424. /// InputSystem.AddDevice(
  1425. /// new InputDeviceDescription
  1426. /// {
  1427. /// interfaceName = "Custom",
  1428. /// product = "Product"
  1429. /// });
  1430. /// </code>
  1431. /// </example>
  1432. /// </remarks>
  1433. public static InputDevice AddDevice(InputDeviceDescription description)
  1434. {
  1435. if (description.empty)
  1436. throw new ArgumentException("Description must not be empty", nameof(description));
  1437. return s_Manager.AddDevice(description);
  1438. }
  1439. /// <summary>
  1440. /// Add the given device back to the system.
  1441. /// </summary>
  1442. /// <param name="device">An input device. If the device is currently already added to
  1443. /// the system (i.e. is in <see cref="devices"/>), the method will do nothing.</param>
  1444. /// <exception cref="ArgumentNullException"></exception>
  1445. /// <remarks>
  1446. /// This can be used when a device has been manually removed with <see cref="RemoveDevice"/>.
  1447. ///
  1448. /// The device will be added to the <see cref="devices"/> list and a notification on
  1449. /// <see cref="onDeviceChange"/> will be triggered.
  1450. ///
  1451. /// It may be tempting to do the following but this will not work:
  1452. ///
  1453. /// <example>
  1454. /// <code>
  1455. /// // This will *NOT* work.
  1456. /// var device = new Gamepad();
  1457. /// InputSystem.AddDevice(device);
  1458. /// </code>
  1459. /// </example>
  1460. ///
  1461. /// <see cref="InputDevice"/>s, like <see cref="InputControl"/>s in general, cannot
  1462. /// simply be instantiated with <c>new</c> but must be created by the input system
  1463. /// instead.
  1464. /// </remarks>
  1465. /// <seealso cref="RemoveDevice"/>
  1466. /// <seealso cref="AddDevice{TDevice}"/>
  1467. /// <seealso cref="devices"/>
  1468. public static void AddDevice(InputDevice device)
  1469. {
  1470. if (device == null)
  1471. throw new ArgumentNullException(nameof(device));
  1472. s_Manager.AddDevice(device);
  1473. }
  1474. /// <summary>
  1475. /// Remove a device from the system such that it no longer receives input and is no longer part of the
  1476. /// set of devices in <see cref="devices"/>.
  1477. /// </summary>
  1478. /// <param name="device">Device to remove. If the device has already been removed (i.e. if <see cref="InputDevice.added"/>
  1479. /// is false), the method does nothing.</param>
  1480. /// <remarks>
  1481. /// Actions that are bound to controls on the device will automatically unbind when the device
  1482. /// is removed.
  1483. ///
  1484. /// When a device is removed, <see cref="onDeviceChange"/> will be triggered with <see cref="InputDeviceChange.Removed"/>.
  1485. /// The device will be removed from <see cref="devices"/> as well as from any device-specific getters such as
  1486. /// <see cref="Gamepad.all"/>.
  1487. /// </remarks>
  1488. /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
  1489. /// <seealso cref="InputDevice.added"/>
  1490. public static void RemoveDevice(InputDevice device)
  1491. {
  1492. s_Manager.RemoveDevice(device);
  1493. }
  1494. /// <summary>
  1495. /// Purge all disconnected devices from <see cref="disconnectedDevices"/>.
  1496. /// </summary>
  1497. /// <remarks>
  1498. /// This will release all references held on to for these devices or any of their controls and will
  1499. /// allow the devices to be reclaimed by the garbage collector.
  1500. /// </remarks>
  1501. /// <seealso cref="disconnectedDevices"/>
  1502. public static void FlushDisconnectedDevices()
  1503. {
  1504. s_Manager.FlushDisconnectedDevices();
  1505. }
  1506. /// <summary>
  1507. /// Return the device with given name or layout <param name="nameOrLayout"/>.
  1508. /// Returns null if no such device currently exists.
  1509. /// </summary>
  1510. /// <param name="nameOrLayout">Unique device name or layout to search for.</param>
  1511. /// <returns>The device matching the given search criteria or null.</returns>
  1512. /// <seealso cref="GetDevice(Type)"/>
  1513. /// <seealso cref="GetDevice{TDevice}"/>
  1514. /// <seealso cref="AddDevice{TDevice}"/>
  1515. public static InputDevice GetDevice(string nameOrLayout)
  1516. {
  1517. return s_Manager.TryGetDevice(nameOrLayout);
  1518. }
  1519. ////REVIEW: this API seems inconsistent with GetDevice(string); both have very different meaning yet very similar signatures
  1520. /// <summary>
  1521. /// Return the most recently used device that is assignable to the given type <typeparamref name="TDevice"/>.
  1522. /// Returns null if no such device currently exists.
  1523. /// </summary>
  1524. /// <typeparam name="TDevice">Type of device to look for.</typeparam>
  1525. /// <returns>The device that is assignable to the given type or null.</returns>
  1526. /// <seealso cref="GetDevice(string)"/>
  1527. /// <seealso cref="GetDevice(Type)"/>
  1528. public static TDevice GetDevice<TDevice>()
  1529. where TDevice : InputDevice
  1530. {
  1531. return (TDevice)GetDevice(typeof(TDevice));
  1532. }
  1533. ////REVIEW: this API seems inconsistent with GetDevice(string); both have very different meaning yet very similar signatures
  1534. /// <summary>
  1535. /// Return the most recently used device that is assignable to the given type <param name="type"/>.
  1536. /// Returns null if no such device currently exists.
  1537. /// </summary>
  1538. /// <param name="type">Type of the device</param>
  1539. /// <returns>The device that is assignable to the given type or null.</returns>
  1540. /// <seealso cref="GetDevice(string)"/>
  1541. /// <seealso cref="GetDevice&lt;TDevice&gt;()"/>
  1542. public static InputDevice GetDevice(Type type)
  1543. {
  1544. InputDevice result = null;
  1545. var lastUpdateTime = -1.0;
  1546. foreach (var device in devices)
  1547. {
  1548. if (!type.IsInstanceOfType(device))
  1549. continue;
  1550. if (result == null || device.m_LastUpdateTimeInternal > lastUpdateTime)
  1551. {
  1552. result = device;
  1553. lastUpdateTime = result.m_LastUpdateTimeInternal;
  1554. }
  1555. }
  1556. return result;
  1557. }
  1558. ////REVIEW: this API seems inconsistent with GetDevice(string); both have very different meaning yet very similar signatures
  1559. /// <summary>
  1560. /// Return the device of the given type <typeparamref name="TDevice"/> that has the
  1561. /// given usage assigned. Returns null if no such device currently exists.
  1562. /// </summary>
  1563. /// <param name="usage">Usage of the device, e.g. "LeftHand".</param>
  1564. /// <typeparam name="TDevice">Type of device to look for.</typeparam>
  1565. /// <returns>The device with the given type and usage or null.</returns>
  1566. /// <remarks>
  1567. /// Devices usages are most commonly employed to "tag" devices for a specific role.
  1568. /// A common scenario, for example, is to distinguish which hand a specific <see cref="XR.XRController"/>
  1569. /// is associated with. However, arbitrary usages can be assigned to devices.
  1570. /// <example>
  1571. /// <code>
  1572. /// // Get the left hand XRController.
  1573. /// var leftHand = InputSystem.GetDevice&lt;XRController&gt;(CommonUsages.leftHand);
  1574. ///
  1575. /// // Mark gamepad #2 as being for player 1.
  1576. /// InputSystem.SetDeviceUsage(Gamepad.all[1], "Player1");
  1577. /// // And later look it up.
  1578. /// var player1Gamepad = InputSystem.GetDevice&lt;Gamepad&gt;(new InternedString("Player1"));
  1579. /// </code>
  1580. /// </example>
  1581. /// </remarks>
  1582. /// <seealso cref="GetDevice(string)"/>
  1583. /// <seealso cref="SetDeviceUsage(InputDevice,string)"/>
  1584. /// <seealso cref="InputControl.usages"/>
  1585. public static TDevice GetDevice<TDevice>(InternedString usage)
  1586. where TDevice : InputDevice
  1587. {
  1588. TDevice result = null;
  1589. var lastUpdateTime = -1.0;
  1590. foreach (var device in devices)
  1591. {
  1592. var deviceOfType = device as TDevice;
  1593. if (deviceOfType == null)
  1594. continue;
  1595. if (!deviceOfType.usages.Contains(usage))
  1596. continue;
  1597. if (result == null || deviceOfType.m_LastUpdateTimeInternal > lastUpdateTime)
  1598. {
  1599. result = deviceOfType;
  1600. lastUpdateTime = result.m_LastUpdateTimeInternal;
  1601. }
  1602. }
  1603. return result;
  1604. }
  1605. /// <summary>
  1606. /// Return the device of the given type <typeparamref name="TDevice"/> that has the
  1607. /// given usage assigned. Returns null if no such device currently exists.
  1608. /// </summary>
  1609. /// <param name="usage">Usage of the device, e.g. "LeftHand".</param>
  1610. /// <typeparam name="TDevice">Type of device to look for.</typeparam>
  1611. /// <returns>The device with the given type and usage or null.</returns>
  1612. /// <remarks>
  1613. /// Devices usages are most commonly employed to "tag" devices for a specific role.
  1614. /// A common scenario, for example, is to distinguish which hand a specific <see cref="XR.XRController"/>
  1615. /// is associated with. However, arbitrary usages can be assigned to devices.
  1616. /// </remarks>
  1617. /// <seealso cref="GetDevice(InternedString)"/>
  1618. /// <seealso cref="SetDeviceUsage(InputDevice,string)"/>
  1619. /// <seealso cref="InputControl.usages"/>
  1620. public static TDevice GetDevice<TDevice>(string usage)
  1621. where TDevice : InputDevice
  1622. {
  1623. return GetDevice<TDevice>(new InternedString(usage));
  1624. }
  1625. /// <summary>
  1626. /// Look up a device by its unique ID.
  1627. /// </summary>
  1628. /// <param name="deviceId">Unique ID of device. Such as given by <see cref="InputEvent.deviceId"/>.</param>
  1629. /// <returns>The device for the given ID or null if no device with the given ID exists (or no longer exists).</returns>
  1630. /// <remarks>
  1631. /// Device IDs are not reused in a given session of the application (or Unity editor).
  1632. /// </remarks>
  1633. /// <seealso cref="InputEvent.deviceId"/>
  1634. /// <seealso cref="InputDevice.deviceId"/>
  1635. /// <seealso cref="IInputRuntime.AllocateDeviceId"/>
  1636. public static InputDevice GetDeviceById(int deviceId)
  1637. {
  1638. return s_Manager.TryGetDeviceById(deviceId);
  1639. }
  1640. /// <summary>
  1641. /// Return the list of devices that have been reported by the <see cref="IInputRuntime">runtime</see>
  1642. /// but could not be matched to any known <see cref="InputControlLayout">layout</see>.
  1643. /// </summary>
  1644. /// <returns>A list of descriptions of devices that could not be recognized.</returns>
  1645. /// <remarks>
  1646. /// If new layouts are added to the system or if additional <see cref="InputDeviceMatcher">matches</see>
  1647. /// are added to existing layouts, devices in this list may appear or disappear.
  1648. /// </remarks>
  1649. /// <seealso cref="InputDeviceMatcher"/>
  1650. /// <seealso cref="RegisterLayoutMatcher"/>
  1651. public static List<InputDeviceDescription> GetUnsupportedDevices()
  1652. {
  1653. var list = new List<InputDeviceDescription>();
  1654. GetUnsupportedDevices(list);
  1655. return list;
  1656. }
  1657. /// <summary>
  1658. /// Populate a list of devices that have been reported by the <see cref="IInputRuntime">runtime</see>
  1659. /// but could not be matched to any known <see cref="InputControlLayout">layout</see>.
  1660. /// </summary>
  1661. /// <param name="descriptions">A list to be populated with descriptions of devices that could not be recognized.</param>
  1662. /// <returns>The number of devices that could not be recognized.</returns>
  1663. /// <remarks>
  1664. /// If new layouts are added to the system or if additional <see cref="InputDeviceMatcher">matches</see>
  1665. /// are added to existing layouts, devices in this list may appear or disappear.
  1666. /// </remarks>
  1667. /// <seealso cref="InputDeviceMatcher"/>
  1668. /// <seealso cref="RegisterLayoutMatcher"/>
  1669. public static int GetUnsupportedDevices(List<InputDeviceDescription> descriptions)
  1670. {
  1671. return s_Manager.GetUnsupportedDevices(descriptions);
  1672. }
  1673. /// <summary>
  1674. /// (Re-)enable the given device.
  1675. /// </summary>
  1676. /// <param name="device">Device to enable. If already enabled, the method will do nothing.</param>
  1677. /// <exception cref="ArgumentNullException"><paramref name="device"/> is <c>null</c>.</exception>
  1678. /// <remarks>
  1679. /// This can be used after a device has been disabled with <see cref="DisableDevice"/> or
  1680. /// with devices that start out in disabled state (usually the case for all <see cref="Sensor"/>
  1681. /// devices).
  1682. ///
  1683. /// When enabled, a device will receive input when available.
  1684. ///
  1685. /// <example>
  1686. /// <code>
  1687. /// // Enable the gyroscope, if present.
  1688. /// if (Gyroscope.current != null)
  1689. /// InputSystem.EnableDevice(Gyroscope.current);
  1690. /// </code>
  1691. /// </example>
  1692. /// </remarks>
  1693. /// <seealso cref="DisableDevice"/>
  1694. /// <seealso cref="InputDevice.enabled"/>
  1695. public static void EnableDevice(InputDevice device)
  1696. {
  1697. s_Manager.EnableOrDisableDevice(device, true);
  1698. }
  1699. /// <summary>
  1700. /// Disable the given device, i.e. "mute" it.
  1701. /// </summary>
  1702. /// <param name="device">Device to disable. If already disabled, the method will do nothing.</param>
  1703. /// <param name="keepSendingEvents">If true, no <see cref="LowLevel.DisableDeviceCommand"/> will be sent
  1704. /// for the device. This means that the backend sending input events will not be notified about the device
  1705. /// being disabled and will thus keep sending events. This can be useful when input is being rerouted from
  1706. /// one device to another. For example, <see cref="TouchSimulation"/> uses this to disable the <see cref="Mouse"/>
  1707. /// while redirecting its events to input on a <see cref="Touchscreen"/>.<br/><br/>This parameter is false by default.</param>
  1708. /// <exception cref="ArgumentNullException"><paramref name="device"/> is <c>null</c>.</exception>
  1709. /// <remarks>
  1710. /// A disabled device will not receive input and will remain in its default state. It will remain
  1711. /// present in the system but without actually feeding input into it.
  1712. ///
  1713. /// Disabling devices is most useful for <see cref="Sensor"/> devices on battery-powered platforms
  1714. /// where having a sensor enabled will increase energy consumption. Sensors will usually start
  1715. /// out in disabled state and can be enabled, when needed, with <see cref="EnableDevice"/> and
  1716. /// disabled again wth this method.
  1717. ///
  1718. /// However, disabling a device can be useful in other situations, too. For example, when simulating
  1719. /// input (say, mouse input) locally from a remote source, it can be desirable to turn off the respective
  1720. /// local device.
  1721. ///
  1722. /// To remove a device altogether, use <see cref="RemoveDevice"/> instead. This will not only silence
  1723. /// input but remove the <see cref="InputDevice"/> instance from the system altogether.
  1724. /// </remarks>
  1725. /// <seealso cref="EnableDevice"/>
  1726. /// <seealso cref="InputDevice.enabled"/>
  1727. public static void DisableDevice(InputDevice device, bool keepSendingEvents = false)
  1728. {
  1729. s_Manager.EnableOrDisableDevice(device, false, keepSendingEvents ? InputManager.DeviceDisableScope.InFrontendOnly : default);
  1730. }
  1731. /// <summary>
  1732. /// Issue a <see cref="RequestSyncCommand"/> on <paramref name="device"/>. This requests the device to
  1733. /// send its current state as an event. If successful, the device will be updated in the next <see cref="InputSystem.Update"/>.
  1734. /// </summary>
  1735. /// <param name="device">An <see cref="InputDevice"/> that is currently part of <see cref="devices"/>.</param>
  1736. /// <returns>True if the request succeeded, false if it fails.</returns>
  1737. /// <remarks>
  1738. /// It depends on the backend/platform implementation whether explicit synchronization is supported. If it is, the method
  1739. /// will return true. If it is not, the method will return false and the request is ignored.
  1740. /// </remarks>
  1741. /// <exception cref="ArgumentNullException"><paramref name="device"/> is <c>null</c>.</exception>
  1742. /// <exception cref="InvalidOperationException"><paramref name="device"/> has not been <see cref="InputDevice.added"/>.</exception>
  1743. /// <seealso cref="RequestSyncCommand"/>
  1744. /// <seealso cref="ResetDevice"/>
  1745. public static bool TrySyncDevice(InputDevice device)
  1746. {
  1747. if (device == null)
  1748. throw new ArgumentNullException(nameof(device));
  1749. if (!device.added)
  1750. throw new InvalidOperationException($"Device '{device}' has not been added");
  1751. return device.RequestSync();
  1752. }
  1753. /// <summary>
  1754. /// Reset the state of the given device.
  1755. /// </summary>
  1756. /// <param name="device">Device to reset. Must be <see cref="InputDevice.added"/> to the system.</param>
  1757. /// <param name="alsoResetDontResetControls">If true, also reset controls that are marked as <see cref="InputControlAttribute.dontReset"/>.
  1758. /// Leads to <see cref="InputDeviceChange.HardReset"/>.</param>
  1759. /// <exception cref="ArgumentNullException"><paramref name="device"/> is <c>null</c>.</exception>
  1760. /// <exception cref="InvalidOperationException"><paramref name="device"/> has not been <see cref="InputDevice.added"/>.</exception>
  1761. /// <remarks>
  1762. /// There are two different kinds of resets performed by the input system: a "soft" reset and a "hard" reset.
  1763. ///
  1764. /// A "hard" reset resets all controls on the device to their default state and also sends a <see cref="RequestResetCommand"/>
  1765. /// to the backend, instructing to also reset its own internal state (if any) to the default.
  1766. ///
  1767. /// A "soft" reset will reset only controls that are not marked as <see cref="InputControlAttribute.noisy"/> and not marked as
  1768. /// <see cref="InputControlAttribute.dontReset"/>. It will also not set a <see cref="RequestResetCommand"/> to the backend,
  1769. /// i.e. the reset will be internal to the input system only (and thus can be partial in nature).
  1770. ///
  1771. /// By default, the method will perform a "soft" reset if <paramref name="device"/> has <see cref="InputControlAttribute.noisy"/>
  1772. /// or <see cref="InputControlAttribute.dontReset"/> controls. If it does not, it will perform a "hard" reset.
  1773. ///
  1774. /// A "hard" reset can be forced by setting <paramref name="alsoResetDontResetControls"/> to true.
  1775. ///
  1776. /// <example>
  1777. /// <code>
  1778. /// // "Soft" reset the mouse. This will leave controls such as the mouse position intact
  1779. /// // but will reset button press states.
  1780. /// InputSystem.ResetDevice(Mouse.current);
  1781. ///
  1782. /// // "Hard" reset the mouse. This will wipe everything and reset the mouse to its default
  1783. /// // state.
  1784. /// InputSystem.ResetDevice(Mouse.current, alsoResetDontResetControls: true);
  1785. /// </code>
  1786. /// </example>
  1787. ///
  1788. /// Resetting a device will trigger a <see cref="InputDeviceChange.SoftReset"/> or <see cref="InputDeviceChange.HardReset"/>
  1789. /// (based on the value of <paramref name="alsoResetDontResetControls"/>) notification on <see cref="onDeviceChange"/>.
  1790. /// Also, all <see cref="InputAction"/>s currently in progress from controls on <paramref name="device"/> will be cancelled
  1791. /// (see <see cref="InputAction.canceled"/>) in a way that guarantees for them to not get triggered. That is, a reset is
  1792. /// semantically different from simply sending an event with default state. Using the latter, a button may be considered as
  1793. /// going from pressed to released whereas with a device reset, the change back to unpressed state will not be considered
  1794. /// a button release (and thus not trigger interactions that are waiting for a button release).
  1795. /// </remarks>
  1796. /// <seealso cref="TrySyncDevice"/>
  1797. /// <seealso cref="InputDeviceChange.HardReset"/>
  1798. /// <seealso cref="InputDeviceChange.SoftReset"/>
  1799. /// <seealso cref="LowLevel.DeviceResetEvent"/>
  1800. public static void ResetDevice(InputDevice device, bool alsoResetDontResetControls = false)
  1801. {
  1802. s_Manager.ResetDevice(device, alsoResetDontResetControls);
  1803. }
  1804. // Not an auto-upgrade as it implies a change in behavior.
  1805. [Obsolete("Use 'ResetDevice' instead.", error: false)]
  1806. public static bool TryResetDevice(InputDevice device)
  1807. {
  1808. if (device == null)
  1809. throw new ArgumentNullException(nameof(device));
  1810. return device.RequestReset();
  1811. }
  1812. ////REVIEW: should there be a global pause state? what about haptics that are issued *while* paused?
  1813. /// <summary>
  1814. /// Pause haptic effect playback on all devices.
  1815. /// </summary>
  1816. /// <remarks>
  1817. /// Calls <see cref="Haptics.IHaptics.PauseHaptics"/> on all <see cref="InputDevice">input devices</see>
  1818. /// that implement the interface.
  1819. /// </remarks>
  1820. /// <seealso cref="ResumeHaptics"/>
  1821. /// <seealso cref="ResetHaptics"/>
  1822. /// <example>
  1823. /// <code>
  1824. /// // When going into the menu from gameplay, pause haptics.
  1825. /// gameplayControls.backAction.onPerformed +=
  1826. /// ctx =>
  1827. /// {
  1828. /// gameplayControls.Disable();
  1829. /// menuControls.Enable();
  1830. /// InputSystem.PauseHaptics();
  1831. /// };
  1832. /// </code>
  1833. /// </example>
  1834. public static void PauseHaptics()
  1835. {
  1836. var devicesList = devices;
  1837. var devicesCount = devicesList.Count;
  1838. for (var i = 0; i < devicesCount; ++i)
  1839. {
  1840. var device = devicesList[i];
  1841. if (device is IHaptics haptics)
  1842. haptics.PauseHaptics();
  1843. }
  1844. }
  1845. /// <summary>
  1846. /// Resume haptic effect playback on all devices.
  1847. /// </summary>
  1848. /// <remarks>
  1849. /// Calls <see cref="Haptics.IHaptics.ResumeHaptics"/> on all <see cref="InputDevice">input devices</see>
  1850. /// that implement the interface.
  1851. /// </remarks>
  1852. /// <seealso cref="PauseHaptics"/>
  1853. public static void ResumeHaptics()
  1854. {
  1855. var devicesList = devices;
  1856. var devicesCount = devicesList.Count;
  1857. for (var i = 0; i < devicesCount; ++i)
  1858. {
  1859. var device = devicesList[i];
  1860. if (device is IHaptics haptics)
  1861. haptics.ResumeHaptics();
  1862. }
  1863. }
  1864. /// <summary>
  1865. /// Stop haptic effect playback on all devices.
  1866. /// </summary>
  1867. /// <remarks>
  1868. /// Will reset haptics effects on all devices to their default state.
  1869. ///
  1870. /// Calls <see cref="Haptics.IHaptics.ResetHaptics"/> on all <see cref="InputDevice">input devices</see>
  1871. /// that implement the interface.
  1872. /// </remarks>
  1873. public static void ResetHaptics()
  1874. {
  1875. var devicesList = devices;
  1876. var devicesCount = devicesList.Count;
  1877. for (var i = 0; i < devicesCount; ++i)
  1878. {
  1879. var device = devicesList[i];
  1880. if (device is IHaptics haptics)
  1881. haptics.ResetHaptics();
  1882. }
  1883. }
  1884. #endregion
  1885. #region Controls
  1886. /// <summary>
  1887. /// Set the usage tag of the given device to <paramref name="usage"/>.
  1888. /// </summary>
  1889. /// <param name="device">Device to set the usage on.</param>
  1890. /// <param name="usage">New usage for the device.</param>
  1891. /// <remarks>
  1892. /// Usages allow to "tag" a specific device such that the tag can then be used in lookups
  1893. /// and bindings. A common use is for identifying the handedness of an <see cref="XR.XRController"/>
  1894. /// but the usages can be arbitrary strings.
  1895. ///
  1896. /// This method either sets the usages of the device to a single string (meaning it will
  1897. /// clear whatever, if any usages, the device has when the method is called) or,
  1898. /// if <paramref name="usage"/> is null or empty, resets the usages of the device
  1899. /// to be empty. To add to a device's set of usages, call <see cref="AddDeviceUsage(InputDevice,string)"/>.
  1900. /// To remove usages from a device, call <see cref="RemoveDeviceUsage(InputDevice,string)"/>.
  1901. ///
  1902. /// The set of usages a device has can be queried with <see cref="InputControl.usages"/> (a device
  1903. /// is an <see cref="InputControl"/> and thus, like controls, has an associated set of usages).
  1904. ///
  1905. /// <example>
  1906. /// <code>
  1907. /// // Tag a gamepad to be associated with player #1.
  1908. /// InputSystem.SetDeviceUsage(myGamepad, "Player1");
  1909. ///
  1910. /// // Create an action that binds to player #1's gamepad specifically.
  1911. /// var action = new InputAction(binding: "&lt;Gamepad&gt;{Player1}/buttonSouth");
  1912. ///
  1913. /// // Move the tag from one gamepad to another.
  1914. /// InputSystem.SetDeviceUsage(myGamepad, null); // Clears usages on 'myGamepad'.
  1915. /// InputSystem.SetDeviceUsage(otherGamepad, "Player1");
  1916. /// </code>
  1917. /// </example>
  1918. /// </remarks>
  1919. /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
  1920. /// <seealso cref="InputControl.usages"/>
  1921. /// <seealso cref="AddDeviceUsage(InputDevice,string)"/>
  1922. /// <seealso cref="RemoveDeviceUsage(InputDevice,string)"/>
  1923. /// <seealso cref="CommonUsages"/>
  1924. /// <seealso cref="InputDeviceChange.UsageChanged"/>
  1925. public static void SetDeviceUsage(InputDevice device, string usage)
  1926. {
  1927. SetDeviceUsage(device, new InternedString(usage));
  1928. }
  1929. /// <summary>
  1930. /// Set the usage tag of the given device to <paramref name="usage"/>.
  1931. /// </summary>
  1932. /// <param name="device">Device to set the usage on.</param>
  1933. /// <param name="usage">New usage for the device.</param>
  1934. /// <remarks>
  1935. /// Usages allow to "tag" a specific device such that the tag can then be used in lookups
  1936. /// and bindings. A common use is for identifying the handedness of an <see cref="XR.XRController"/>
  1937. /// but the usages can be arbitrary strings.
  1938. ///
  1939. /// This method either sets the usages of the device to a single string (meaning it will
  1940. /// clear whatever, if any usages, the device has when the method is called) or,
  1941. /// if <paramref name="usage"/> is null or empty, resets the usages of the device
  1942. /// to be empty. To add to a device's set of usages, call <see cref="AddDeviceUsage(InputDevice,InternedString)"/>.
  1943. /// To remove usages from a device, call <see cref="RemoveDeviceUsage(InputDevice,InternedString)"/>.
  1944. ///
  1945. /// The set of usages a device has can be queried with <see cref="InputControl.usages"/> (a device
  1946. /// is an <see cref="InputControl"/> and thus, like controls, has an associated set of usages).
  1947. ///
  1948. /// If the set of usages on the device changes as a result of calling this method, <see cref="onDeviceChange"/>
  1949. /// will be triggered with <see cref="InputDeviceChange.UsageChanged"/>.
  1950. ///
  1951. /// <example>
  1952. /// <code>
  1953. /// // Tag a gamepad to be associated with player #1.
  1954. /// InputSystem.SetDeviceUsage(myGamepad, new InternedString("Player1"));
  1955. ///
  1956. /// // Create an action that binds to player #1's gamepad specifically.
  1957. /// var action = new InputAction(binding: "&lt;Gamepad&gt;{Player1}/buttonSouth");
  1958. ///
  1959. /// // Move the tag from one gamepad to another.
  1960. /// InputSystem.SetDeviceUsage(myGamepad, null); // Clears usages on 'myGamepad'.
  1961. /// InputSystem.SetDeviceUsage(otherGamepad, new InternedString("Player1"));
  1962. /// </code>
  1963. /// </example>
  1964. /// </remarks>
  1965. /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
  1966. /// <seealso cref="InputControl.usages"/>
  1967. /// <seealso cref="AddDeviceUsage(InputDevice,InternedString)"/>
  1968. /// <seealso cref="RemoveDeviceUsage(InputDevice,InternedString)"/>
  1969. /// <seealso cref="CommonUsages"/>
  1970. /// <seealso cref="InputDeviceChange.UsageChanged"/>
  1971. public static void SetDeviceUsage(InputDevice device, InternedString usage)
  1972. {
  1973. s_Manager.SetDeviceUsage(device, usage);
  1974. }
  1975. /// <summary>
  1976. /// Add a usage tag to the given device.
  1977. /// </summary>
  1978. /// <param name="device">Device to add the usage to.</param>
  1979. /// <param name="usage">New usage to add to the device.</param>
  1980. /// <remarks>
  1981. /// Usages allow to "tag" a specific device such that the tag can then be used in lookups
  1982. /// and bindings. A common use is for identifying the handedness of an <see cref="XR.XRController"/>
  1983. /// but the usages can be arbitrary strings.
  1984. ///
  1985. /// This method adds a new usage to the device's set of usages. If the device already has
  1986. /// the given usage, the method does nothing. To instead set the device's usages to a single
  1987. /// one, use <see cref="SetDeviceUsage(InputDevice,string)"/>. To remove usages from a device,
  1988. /// call <see cref="RemoveDeviceUsage(InputDevice,string)"/>.
  1989. ///
  1990. /// The set of usages a device has can be queried with <see cref="InputControl.usages"/> (a device
  1991. /// is an <see cref="InputControl"/> and thus, like controls, has an associated set of usages).
  1992. ///
  1993. /// If the set of usages on the device changes as a result of calling this method, <see cref="onDeviceChange"/>
  1994. /// will be triggered with <see cref="InputDeviceChange.UsageChanged"/>.
  1995. /// </remarks>
  1996. /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
  1997. /// <exception cref="ArgumentException"><paramref name="usage"/> is null or empty.</exception>
  1998. /// <seealso cref="InputControl.usages"/>
  1999. /// <seealso cref="SetDeviceUsage(InputDevice,string)"/>
  2000. /// <seealso cref="RemoveDeviceUsage(InputDevice,string)"/>
  2001. /// <seealso cref="CommonUsages"/>
  2002. /// <seealso cref="InputDeviceChange.UsageChanged"/>
  2003. public static void AddDeviceUsage(InputDevice device, string usage)
  2004. {
  2005. s_Manager.AddDeviceUsage(device, new InternedString(usage));
  2006. }
  2007. /// <summary>
  2008. /// Add a usage tag to the given device.
  2009. /// </summary>
  2010. /// <param name="device">Device to add the usage to.</param>
  2011. /// <param name="usage">New usage to add to the device.</param>
  2012. /// <remarks>
  2013. /// Usages allow to "tag" a specific device such that the tag can then be used in lookups
  2014. /// and bindings. A common use is for identifying the handedness of an <see cref="XR.XRController"/>
  2015. /// but the usages can be arbitrary strings.
  2016. ///
  2017. /// This method adds a new usage to the device's set of usages. If the device already has
  2018. /// the given usage, the method does nothing. To instead set the device's usages to a single
  2019. /// one, use <see cref="SetDeviceUsage(InputDevice,InternedString)"/>. To remove usages from a device,
  2020. /// call <see cref="RemoveDeviceUsage(InputDevice,InternedString)"/>.
  2021. ///
  2022. /// The set of usages a device has can be queried with <see cref="InputControl.usages"/> (a device
  2023. /// is an <see cref="InputControl"/> and thus, like controls, has an associated set of usages).
  2024. ///
  2025. /// If the set of usages on the device changes as a result of calling this method, <see cref="onDeviceChange"/>
  2026. /// will be triggered with <see cref="InputDeviceChange.UsageChanged"/>.
  2027. /// </remarks>
  2028. /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
  2029. /// <exception cref="ArgumentException"><paramref name="usage"/> is empty.</exception>
  2030. /// <seealso cref="InputControl.usages"/>
  2031. /// <seealso cref="SetDeviceUsage(InputDevice,InternedString)"/>
  2032. /// <seealso cref="RemoveDeviceUsage(InputDevice,InternedString)"/>
  2033. /// <seealso cref="CommonUsages"/>
  2034. /// <seealso cref="InputDeviceChange.UsageChanged"/>
  2035. public static void AddDeviceUsage(InputDevice device, InternedString usage)
  2036. {
  2037. s_Manager.AddDeviceUsage(device, usage);
  2038. }
  2039. /// <summary>
  2040. /// Remove a usage tag from the given device.
  2041. /// </summary>
  2042. /// <param name="device">Device to remove the usage from.</param>
  2043. /// <param name="usage">Usage to remove from the device.</param>
  2044. /// <remarks>
  2045. /// This method removes an existing usage from the given device. If the device does not
  2046. /// have the given usage tag, the method does nothing. Use <see cref="SetDeviceUsage(InputDevice,string)"/>
  2047. /// or <see cref="AddDeviceUsage(InputDevice,string)"/> to add usages to a device.
  2048. ///
  2049. /// The set of usages a device has can be queried with <see cref="InputControl.usages"/> (a device
  2050. /// is an <see cref="InputControl"/> and thus, like controls, has an associated set of usages).
  2051. ///
  2052. /// If the set of usages on the device changes as a result of calling this method, <see cref="onDeviceChange"/>
  2053. /// will be triggered with <see cref="InputDeviceChange.UsageChanged"/>.
  2054. /// </remarks>
  2055. /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
  2056. /// <exception cref="ArgumentException"><paramref name="usage"/> is null or empty.</exception>
  2057. /// <seealso cref="InputControl.usages"/>
  2058. /// <seealso cref="SetDeviceUsage(InputDevice,string)"/>
  2059. /// <seealso cref="AddDeviceUsage(InputDevice,string)"/>
  2060. /// <seealso cref="CommonUsages"/>
  2061. /// <seealso cref="InputDeviceChange.UsageChanged"/>
  2062. public static void RemoveDeviceUsage(InputDevice device, string usage)
  2063. {
  2064. s_Manager.RemoveDeviceUsage(device, new InternedString(usage));
  2065. }
  2066. /// <summary>
  2067. /// Remove a usage tag from the given device.
  2068. /// </summary>
  2069. /// <param name="device">Device to remove the usage from.</param>
  2070. /// <param name="usage">Usage to remove from the device.</param>
  2071. /// <remarks>
  2072. /// This method removes an existing usage from the given device. If the device does not
  2073. /// have the given usage tag, the method does nothing. Use <see cref="SetDeviceUsage(InputDevice,InternedString)"/>
  2074. /// or <see cref="AddDeviceUsage(InputDevice,InternedString)"/> to add usages to a device.
  2075. ///
  2076. /// The set of usages a device has can be queried with <see cref="InputControl.usages"/> (a device
  2077. /// is an <see cref="InputControl"/> and thus, like controls, has an associated set of usages).
  2078. ///
  2079. /// If the set of usages on the device changes as a result of calling this method, <see cref="onDeviceChange"/>
  2080. /// will be triggered with <see cref="InputDeviceChange.UsageChanged"/>.
  2081. /// </remarks>
  2082. /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
  2083. /// <exception cref="ArgumentException"><paramref name="usage"/> is empty.</exception>
  2084. /// <seealso cref="InputControl.usages"/>
  2085. /// <seealso cref="SetDeviceUsage(InputDevice,InternedString)"/>
  2086. /// <seealso cref="AddDeviceUsage(InputDevice,InternedString)"/>
  2087. /// <seealso cref="CommonUsages"/>
  2088. /// <seealso cref="InputDeviceChange.UsageChanged"/>
  2089. public static void RemoveDeviceUsage(InputDevice device, InternedString usage)
  2090. {
  2091. s_Manager.RemoveDeviceUsage(device, usage);
  2092. }
  2093. /// <summary>
  2094. /// Find the first control that matches the given control path.
  2095. /// </summary>
  2096. /// <param name="path">Path of a control, e.g. <c>"&lt;Gamepad&gt;/buttonSouth"</c>. See <see cref="InputControlPath"/>
  2097. /// for details.</param>
  2098. /// <returns>The first control that matches the given path or <c>null</c> if no control matches.</returns>
  2099. /// <exception cref="ArgumentNullException"><paramref name="path"/> is <c>null</c> or empty.</exception>
  2100. /// <remarks>
  2101. /// If multiple controls match the given path, which result is considered the first is indeterminate.
  2102. ///
  2103. /// <example>
  2104. /// <code>
  2105. /// // Add gamepad.
  2106. /// InputSystem.AddDevice&lt;Gamepad&gt;();
  2107. ///
  2108. /// // Look up various controls on it.
  2109. /// var aButton = InputSystem.FindControl("&lt;Gamepad&gt;/buttonSouth");
  2110. /// var leftStickX = InputSystem.FindControl("*/leftStick/x");
  2111. /// var bButton = InputSystem.FindControl"*/{back}");
  2112. ///
  2113. /// // This one returns the gamepad itself as devices are also controls.
  2114. /// var gamepad = InputSystem.FindControl("&lt;Gamepad&gt;");
  2115. /// </code>
  2116. /// </example>
  2117. /// </remarks>
  2118. /// <seealso cref="InputControlPath"/>
  2119. /// <seealso cref="InputControl.path"/>
  2120. public static InputControl FindControl(string path)
  2121. {
  2122. if (string.IsNullOrEmpty(path))
  2123. throw new ArgumentNullException(nameof(path));
  2124. var devices = s_Manager.devices;
  2125. var numDevices = devices.Count;
  2126. for (var i = 0; i < numDevices; ++i)
  2127. {
  2128. var device = devices[i];
  2129. var control = InputControlPath.TryFindControl(device, path);
  2130. if (control != null)
  2131. return control;
  2132. }
  2133. return null;
  2134. }
  2135. /// <summary>
  2136. /// Find all controls that match the given <see cref="InputControlPath">control path</see>.
  2137. /// </summary>
  2138. /// <param name="path">Control path to search for</param>
  2139. /// <returns>List of <see cref="InputControl"/> which matched the given search criteria</returns>
  2140. /// <example>
  2141. /// <code>
  2142. /// // Find all gamepads (literally: that use the "Gamepad" layout).
  2143. /// InputSystem.FindControls("&lt;Gamepad&gt;");
  2144. ///
  2145. /// // Find all sticks on all gamepads.
  2146. /// InputSystem.FindControls("&lt;Gamepad&gt;/*stick");
  2147. ///
  2148. /// // Same but filter stick by type rather than by name.
  2149. /// InputSystem.FindControls&lt;StickControl&gt;("&lt;Gamepad&gt;/*");
  2150. /// </code>
  2151. /// </example>
  2152. /// <seealso cref="FindControls{TControl}(string)"/>
  2153. /// <seealso cref="FindControls{TControl}(string,ref UnityEngine.InputSystem.InputControlList{TControl})"/>
  2154. public static InputControlList<InputControl> FindControls(string path)
  2155. {
  2156. return FindControls<InputControl>(path);
  2157. }
  2158. /// <summary>
  2159. /// Find all controls that match the given <see cref="InputControlPath">control path</see>.
  2160. /// </summary>
  2161. /// <param name="path">Control path to search for</param>
  2162. /// <typeparam name="TControl">Type of control <see cref="InputControl"/>.</typeparam>
  2163. /// <returns>Generic list of <see cref="InputControl"/> which matched the given search criteria</returns>
  2164. /// <seealso cref="FindControls{InputControl}(string)"/>
  2165. /// <seealso cref="FindControls{TControl}(string,ref UnityEngine.InputSystem.InputControlList{TControl})"/>
  2166. public static InputControlList<TControl> FindControls<TControl>(string path)
  2167. where TControl : InputControl
  2168. {
  2169. var list = new InputControlList<TControl>();
  2170. FindControls(path, ref list);
  2171. return list;
  2172. }
  2173. /// <summary>
  2174. /// Populate a list with all controls that match the given <see cref="InputControlPath">control path</see>.
  2175. /// </summary>
  2176. /// <param name="path">Control path to search for</param>
  2177. /// <param name="controls">Generic list of <see cref="InputControl"/> to populate with the search results</param>
  2178. /// <typeparam name="TControl">Type of control <see cref="InputControl"/>.</typeparam>
  2179. /// <returns>Count of controls which matched the given search criteria</returns>
  2180. /// <seealso cref="FindControls{TControl}(string)"/>
  2181. /// <seealso cref="FindControls{TControl}(string,ref UnityEngine.InputSystem.InputControlList{TControl})"/>
  2182. public static int FindControls<TControl>(string path, ref InputControlList<TControl> controls)
  2183. where TControl : InputControl
  2184. {
  2185. return s_Manager.GetControls(path, ref controls);
  2186. }
  2187. #endregion
  2188. #region Events
  2189. internal static bool isProcessingEvents => s_Manager.isProcessingEvents;
  2190. /// <summary>
  2191. /// Called during <see cref="Update"/> for each event that is processed.
  2192. /// </summary>
  2193. /// <remarks>
  2194. /// Every time the input system updates (see <see cref="InputSettings.updateMode"/>
  2195. /// or <see cref="Update"/> for details about when and how this happens),
  2196. /// it flushes all events from the internal event buffer.
  2197. ///
  2198. /// As the Input System reads events from the buffer one by one, it will trigger this
  2199. /// callback for each event which originates from a recognized device, before then proceeding
  2200. /// to process the event. If any of the callbacks sets <see cref="InputEvent.handled"/>
  2201. /// to true, the event will be skipped and ignored.
  2202. ///
  2203. /// Note that a device that is disabled (see <see cref="InputDevice.enabled"/>) may still get
  2204. /// this event signalled for it. A <see cref="DisableDeviceCommand"/> will usually be sent to
  2205. /// backends when a device is disabled but a backend may or may not respond to the command and
  2206. /// thus may or may not keep sending events for the device.
  2207. ///
  2208. /// Note that the Input System does NOT sort events by timestamps (<see cref="InputEvent.time"/>).
  2209. /// Instead, they are consumed in the order they are produced. This means that they
  2210. /// will also surface on this callback in that order.
  2211. ///
  2212. /// <example>
  2213. /// <code>
  2214. /// // Treat left+right mouse button as middle mouse button.
  2215. /// // (Note: This example is more for demonstrative purposes; it isn't necessarily a good use case)
  2216. /// InputSystem.onEvent +=
  2217. /// (eventPtr, device) =>
  2218. /// {
  2219. /// // Only deal with state events.
  2220. /// if (!eventPtr.IsA&lt;StateEvent&gt;())
  2221. /// return;
  2222. ///
  2223. /// if (!(device is Mouse mouse))
  2224. /// return;
  2225. ///
  2226. /// mouse.leftButton.ReadValueFromEvent(eventPtr, out var lmbDown);
  2227. /// mouse.rightButton.ReadValueFromEvent(eventPtr, out var rmbDown);
  2228. ///
  2229. /// if (lmbDown > 0 &amp;&amp; rmbDown > 0)
  2230. /// mouse.middleButton.WriteValueIntoEvent(1f, eventPtr);
  2231. /// };
  2232. /// </code>
  2233. /// </example>
  2234. ///
  2235. /// The property returns an <see cref="InputEventListener"/> struct that, beyond adding and removing
  2236. /// callbacks, can be used to flexibly listen in on the event stream.
  2237. ///
  2238. /// <example>
  2239. /// <code>
  2240. /// // Listen for mouse events.
  2241. /// InputSystem.onEvent
  2242. /// .ForDevice(Mouse.current)
  2243. /// .Call(e => Debug.Log("Mouse event"));
  2244. /// </code>
  2245. /// </example>
  2246. ///
  2247. /// If you are looking for a way to capture events, <see cref="InputEventTrace"/> may be of
  2248. /// interest and an alternative to directly hooking into this event.
  2249. ///
  2250. /// If you are looking to monitor changes to specific input controls, state change monitors
  2251. /// (see <see cref="InputState.AddChangeMonitor(InputControl,IInputStateChangeMonitor,long,uint)"/>
  2252. /// are usually a more efficient and convenient way to set this up.
  2253. /// </remarks>
  2254. /// <exception cref="ArgumentNullException">Delegate reference is <c>null</c>.</exception>
  2255. /// <seealso cref="QueueEvent(InputEventPtr)"/>
  2256. /// <seealso cref="InputEvent"/>
  2257. /// <seealso cref="Update"/>
  2258. /// <seealso cref="InputSettings.updateMode"/>
  2259. public static InputEventListener onEvent
  2260. {
  2261. // The listener syntax is an artificial struct. Setting it has no effect.
  2262. // Its only purpose is to give us access to both the += and -= syntax of C# events
  2263. // and at the same time provide a springboard into IObservable.
  2264. get => default;
  2265. // ReSharper disable once ValueParameterNotUsed
  2266. set {}
  2267. }
  2268. /// <summary>
  2269. /// Listen through <see cref="onEvent"/> for a button to be pressed.
  2270. /// </summary>
  2271. /// <remarks>
  2272. /// The listener will get triggered whenever a <see cref="ButtonControl"/> on any device in the list of <see cref="devices"/>
  2273. /// goes from not being pressed to being pressed.
  2274. ///
  2275. /// <example>
  2276. /// <code>
  2277. /// // Response to the first button press. Calls our delegate
  2278. /// // and then immediately stops listening.
  2279. /// InputSystem.onAnyButtonPress
  2280. /// .CallOnce(ctrl => Debug.Log($"Button {ctrl} was pressed"));
  2281. /// </code>
  2282. /// </example>
  2283. ///
  2284. /// Note that the listener will get triggered from the first button that was found in a pressed state in a
  2285. /// given <see cref="InputEvent"/>. If multiple buttons are pressed in an event, the listener will not
  2286. /// get triggered multiple times. To get all button presses in an event, use <see cref="InputControlExtensions.GetAllButtonPresses"/>
  2287. /// and instead listen directly through <see cref="onEvent"/>.
  2288. ///
  2289. /// <example>
  2290. /// <code>
  2291. /// InputSystem.onEvent
  2292. /// .Where(e => e.HasButtonPress())
  2293. /// .CallOnce(eventPtr =>
  2294. /// {
  2295. /// foreach (var button in l.eventPtr.GetAllButtonPresses())
  2296. /// Debug.Log($"Button {button} was pressed");
  2297. /// });
  2298. /// </code>
  2299. /// </example>
  2300. ///
  2301. /// There is a certain overhead to listening for button presses so it is best to have listeners
  2302. /// installed only while the information is actually needed.
  2303. ///
  2304. /// <example>
  2305. /// <code>
  2306. /// // Script that will spawn a new player when a button on a device is pressed.
  2307. /// public class JoinPlayerOnPress : MonoBehaviour
  2308. /// {
  2309. /// // We instantiate this GameObject to create a new player object.
  2310. /// // Expected to have a PlayerInput component in its hierarchy.
  2311. /// public GameObject playerPrefab;
  2312. ///
  2313. /// // We want to remove the event listener we install through InputSystem.onAnyButtonPress
  2314. /// // after we're done so remember it here.
  2315. /// private IDisposable m_EventListener;
  2316. ///
  2317. /// // When enabled, we install our button press listener.
  2318. /// void OnEnable()
  2319. /// {
  2320. /// // Start listening.
  2321. /// m_EventListener =
  2322. /// InputSystem.onAnyButtonPress
  2323. /// .Call(OnButtonPressed)
  2324. /// }
  2325. ///
  2326. /// // When disabled, we remove our button press listener.
  2327. /// void OnDisable()
  2328. /// {
  2329. /// m_EventListener.Dispose();
  2330. /// }
  2331. ///
  2332. /// void OnButtonPressed(InputControl button)
  2333. /// {
  2334. /// var device = button.device;
  2335. ///
  2336. /// // Ignore presses on devices that are already used by a player.
  2337. /// if (PlayerInput.FindFirstPairedToDevice(device) != null)
  2338. /// return;
  2339. ///
  2340. /// // Create a new player.
  2341. /// var player = PlayerInput.Instantiate(playerPrefab, pairWithDevice: device);
  2342. ///
  2343. /// // If the player did not end up with a valid input setup,
  2344. /// // unjoin the player.
  2345. /// if (player.hasMissingRequiredDevices)
  2346. /// Destroy(player);
  2347. ///
  2348. /// // If we only want to join a single player, could uninstall our listener here
  2349. /// // or use CallOnce() instead of Call() when we set it up.
  2350. /// }
  2351. /// }
  2352. /// </code>
  2353. /// </example>
  2354. /// </remarks>
  2355. /// <seealso cref="ButtonControl.isPressed"/>
  2356. /// <seealso cref="onEvent"/>
  2357. public static IObservable<InputControl> onAnyButtonPress =>
  2358. onEvent
  2359. .Select(e => e.GetFirstButtonPressOrNull()).Where(c => c != null);
  2360. /// <summary>
  2361. /// Add an event to the internal event queue.
  2362. /// </summary>
  2363. /// <param name="eventPtr">Event to add to the internal event buffer.</param>
  2364. /// <exception cref="ArgumentException"><paramref name="eventPtr"/> is not
  2365. /// valid (see <see cref="InputEventPtr.valid"/>).</exception>
  2366. /// <exception cref="InvalidOperationException">The method was called from
  2367. /// within event processing more than 1000 times. To avoid deadlocking, this
  2368. /// results in an exception being thrown.</exception>
  2369. /// <remarks>
  2370. /// The event will be copied in full to the internal event buffer meaning that
  2371. /// you can release memory for the event after it has been queued. The internal event
  2372. /// buffer is flushed on the next input system update (see <see cref="Update"/>).
  2373. /// Note that if input is process in <c>FixedUpdate()</c> (see <see cref="InputSettings.updateMode"/>),
  2374. /// then the event may not get processed until its <see cref="InputEvent.time"/> timestamp
  2375. /// is within the update window of the input system.
  2376. ///
  2377. /// As part of queuing, the event will receive its own unique ID (see <see cref="InputEvent.eventId"/>).
  2378. /// Note that this ID will be written into the memory buffer referenced by <paramref cref="eventPtr"/>
  2379. /// meaning that after calling <c>QueueEvent</c>, you will see the event ID with which the event
  2380. /// was queued.
  2381. ///
  2382. /// Events that are queued during event processing will get processed in the same update.
  2383. /// This happens, for example, when queuing input from within <see cref="onEvent"/> or from
  2384. /// action callbacks such as <see cref="InputAction.performed"/>.
  2385. ///
  2386. /// The total size of <see cref="InputEvent"/>s processed in a single update is limited by
  2387. /// <see cref="InputSettings.maxEventBytesPerUpdate"/>. This also prevents deadlocks when
  2388. /// each processing of an event leads to one or more additional events getting queued.
  2389. ///
  2390. /// <example>
  2391. /// <code>
  2392. /// // Queue an input event on the first gamepad.
  2393. /// var gamepad = Gamepad.all[0];
  2394. /// using (StateEvent.From(gamepad, out var eventPtr))
  2395. /// {
  2396. /// gamepad.leftStick.WriteValueIntoEvent(new Vector2(0.123f, 0.234f), eventPtr);
  2397. /// InputSystem.QueueEvent(eventPtr);
  2398. /// }
  2399. /// </code>
  2400. /// </example>
  2401. /// </remarks>
  2402. /// <seealso cref="Update"/>
  2403. /// <seealso cref="onEvent"/>
  2404. /// <seealso cref="onBeforeUpdate"/>
  2405. /// <seealso cref="InputEvent"/>
  2406. public static void QueueEvent(InputEventPtr eventPtr)
  2407. {
  2408. if (!eventPtr.valid)
  2409. throw new ArgumentException("Received a null event pointer", nameof(eventPtr));
  2410. s_Manager.QueueEvent(eventPtr);
  2411. }
  2412. /// <summary>
  2413. /// Add an event to the internal event queue.
  2414. /// </summary>
  2415. /// <typeparam name="TEvent">Type of event to look enqueue.</typeparam>
  2416. /// <param name="inputEvent">Event to add to the internal event buffer.</param>
  2417. /// <remarks>
  2418. /// The event will be copied in full to the internal event buffer. The internal event
  2419. /// buffer is flushed on the next input system update (see <see cref="Update"/>).
  2420. /// Note that if input is process in <c>FixedUpdate()</c> (see <see cref="InputSettings.updateMode"/>),
  2421. /// then the event may not get processed until its <see cref="InputEvent.time"/> timestamp
  2422. /// is within the update window of the input system.
  2423. ///
  2424. /// As part of queuing, the event will receive its own unique ID (see <see cref="InputEvent.eventId"/>).
  2425. /// Note that this ID will be written into <paramref name="inputEvent"/>
  2426. /// meaning that after calling this method, you will see the event ID with which the event
  2427. /// was queued.
  2428. ///
  2429. /// <example>
  2430. /// <code>
  2431. /// // Queue a disconnect event on the first gamepad.
  2432. /// var inputEvent = DeviceRemoveEvent(Gamepad.all[0].deviceId);
  2433. /// InputSystem.QueueEvent(inputEvent);
  2434. /// </code>
  2435. /// </example>
  2436. /// </remarks>
  2437. /// <seealso cref="Update"/>
  2438. /// <seealso cref="onEvent"/>
  2439. /// <seealso cref="onBeforeUpdate"/>
  2440. public static void QueueEvent<TEvent>(ref TEvent inputEvent)
  2441. where TEvent : struct, IInputEventTypeInfo
  2442. {
  2443. s_Manager.QueueEvent(ref inputEvent);
  2444. }
  2445. ////REVIEW: consider moving these out into extension methods in UnityEngine.InputSystem.LowLevel
  2446. ////TODO: find a more elegant solution for this
  2447. // Mono will ungracefully poop exceptions if we try to use LayoutKind.Explicit in generic
  2448. // structs. So we can't just stuff a generic TState into a StateEvent<TState> and enforce
  2449. // proper layout. Thus the jumping through lots of ugly hoops here.
  2450. private unsafe struct StateEventBuffer
  2451. {
  2452. public StateEvent stateEvent;
  2453. public const int kMaxSize = 512;
  2454. public fixed byte data[kMaxSize - 1]; // StateEvent already adds one.
  2455. }
  2456. /// <summary>
  2457. /// Queue a <see cref="StateEvent"/> to update the input state of the given device.
  2458. /// </summary>
  2459. /// <param name="device">Device whose input state to update</param>
  2460. /// <param name="state"></param>
  2461. /// <param name="time">Timestamp for the event. If not supplied, the current time is used. Note
  2462. /// that if the given time is in the future and events processed in
  2463. /// <a href="https://docs.unity3d.com/ScriptReference/MonoBehaviour.FixedUpdate.html">FixedUpdate</a> (see <see cref="InputSettings.updateMode"/>),
  2464. /// the event will only get processed once the actual time has caught up with the given time.</param>
  2465. /// <typeparam name="TState">Type of input state, such as <see cref="MouseState"/>. Must match the expected
  2466. /// type of state of <paramref name="device"/>.</typeparam>
  2467. /// <remarks>
  2468. /// The given state must match exactly what is expected by the given device. If unsure, an alternative
  2469. /// is to grab the state as an event directly from the device using <see
  2470. /// cref="StateEvent.From(InputDevice,out InputEventPtr,Unity.Collections.Allocator)"/> which can then
  2471. /// be queued using <see cref="QueueEvent(InputEventPtr)"/>.
  2472. ///
  2473. /// <example>
  2474. /// <code>
  2475. /// // Allocates temporary, unmanaged memory for the event.
  2476. /// // using statement automatically disposes the memory once we have queued the event.
  2477. /// using (StateEvent.From(Mouse.current, out var eventPtr))
  2478. /// {
  2479. /// // Use controls on mouse to write values into event.
  2480. /// Mouse.current.position.WriteValueIntoEvent(new Vector(123, 234), eventPtr);
  2481. ///
  2482. /// // Queue event.
  2483. /// InputSystem.QueueEvent(eventPtr);
  2484. /// }
  2485. /// </code>
  2486. /// </example>
  2487. ///
  2488. /// The event will only be queued and not processed right away. This means that the state of
  2489. /// <paramref name="device"/> will not change immediately as a result of calling this method. Instead,
  2490. /// the event will be processed as part of the next input update.
  2491. ///
  2492. /// Note that this method updates the complete input state of the device including all of its
  2493. /// controls. To update just part of the state of a device, you can use <see cref="QueueDeltaStateEvent{TDelta}"/>
  2494. /// (however, note that there are some restrictions; see documentation).
  2495. /// <example>
  2496. /// <code>
  2497. /// InputSystem.QueueStateEvent(Mouse.current, new MouseState { position = new Vector(123, 234) });
  2498. /// </code>
  2499. /// </example>
  2500. /// </remarks>
  2501. /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
  2502. /// <exception cref="InvalidOperationException"><paramref name="device"/> has not been added to the system
  2503. /// (<see cref="AddDevice(InputDevice)"/>) and thus cannot receive events.</exception>
  2504. /// <exception cref="ArgumentException"></exception>
  2505. public static unsafe void QueueStateEvent<TState>(InputDevice device, TState state, double time = -1)
  2506. where TState : struct, IInputStateTypeInfo
  2507. {
  2508. if (device == null)
  2509. throw new ArgumentNullException(nameof(device));
  2510. // Make sure device is actually in the system.
  2511. if (device.m_DeviceIndex == InputDevice.kInvalidDeviceIndex)
  2512. throw new InvalidOperationException(
  2513. $"Cannot queue state event for device '{device}' because device has not been added to system");
  2514. ////REVIEW: does it make more sense to go off the 'stateBlock' on the device and let that determine size?
  2515. var stateSize = (uint)UnsafeUtility.SizeOf<TState>();
  2516. if (stateSize > StateEventBuffer.kMaxSize)
  2517. throw new ArgumentException(
  2518. $"Size of '{typeof(TState).Name}' exceeds maximum supported state size of {StateEventBuffer.kMaxSize}",
  2519. nameof(state));
  2520. var eventSize = UnsafeUtility.SizeOf<StateEvent>() + stateSize - StateEvent.kStateDataSizeToSubtract;
  2521. if (time < 0)
  2522. time = InputRuntime.s_Instance.currentTime;
  2523. else
  2524. time += InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup;
  2525. StateEventBuffer eventBuffer;
  2526. eventBuffer.stateEvent =
  2527. new StateEvent
  2528. {
  2529. baseEvent = new InputEvent(StateEvent.Type, (int)eventSize, device.deviceId, time),
  2530. stateFormat = state.format
  2531. };
  2532. var ptr = eventBuffer.stateEvent.stateData;
  2533. UnsafeUtility.MemCpy(ptr, UnsafeUtility.AddressOf(ref state), stateSize);
  2534. s_Manager.QueueEvent(ref eventBuffer.stateEvent);
  2535. }
  2536. private unsafe struct DeltaStateEventBuffer
  2537. {
  2538. public DeltaStateEvent stateEvent;
  2539. public const int kMaxSize = 512;
  2540. public fixed byte data[kMaxSize - 1]; // DeltaStateEvent already adds one.
  2541. }
  2542. /// <summary>
  2543. /// Queue a <see cref="DeltaStateEvent"/> to update part of the input state of the given device.
  2544. /// </summary>
  2545. /// <param name="control">Control on a device to update state of.</param>
  2546. /// <param name="delta">New state for the control. Type of state must match the state of the control.</param>
  2547. /// <param name="time"></param>
  2548. /// <typeparam name="TDelta"></typeparam>
  2549. /// <exception cref="ArgumentNullException"><paramref name="control"/> is null.</exception>
  2550. /// <exception cref="InvalidOperationException"></exception>
  2551. /// <exception cref="ArgumentException"></exception>
  2552. public static unsafe void QueueDeltaStateEvent<TDelta>(InputControl control, TDelta delta, double time = -1)
  2553. where TDelta : struct
  2554. {
  2555. if (control == null)
  2556. throw new ArgumentNullException(nameof(control));
  2557. if (control.stateBlock.bitOffset != 0)
  2558. throw new InvalidOperationException(
  2559. $"Cannot send delta state events against bitfield controls: {control}");
  2560. // Make sure device is actually in the system.
  2561. var device = control.device;
  2562. if (device.m_DeviceIndex == InputDevice.kInvalidDeviceIndex)
  2563. throw new InvalidOperationException(
  2564. $"Cannot queue state event for control '{control}' on device '{device}' because device has not been added to system");
  2565. if (time < 0)
  2566. time = InputRuntime.s_Instance.currentTime;
  2567. else
  2568. time += InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup;
  2569. var deltaSize = (uint)UnsafeUtility.SizeOf<TDelta>();
  2570. if (deltaSize > DeltaStateEventBuffer.kMaxSize)
  2571. throw new ArgumentException(
  2572. $"Size of state delta '{typeof(TDelta).Name}' exceeds maximum supported state size of {DeltaStateEventBuffer.kMaxSize}",
  2573. nameof(delta));
  2574. ////TODO: recognize a matching C# representation of a state format and convert to what we expect for trivial cases
  2575. if (deltaSize != control.stateBlock.alignedSizeInBytes)
  2576. throw new ArgumentException(
  2577. $"Size {deltaSize} of delta state of type {typeof(TDelta).Name} provided for control '{control}' does not match size {control.stateBlock.alignedSizeInBytes} of control",
  2578. nameof(delta));
  2579. var eventSize = UnsafeUtility.SizeOf<DeltaStateEvent>() + deltaSize - 1;
  2580. DeltaStateEventBuffer eventBuffer;
  2581. eventBuffer.stateEvent =
  2582. new DeltaStateEvent
  2583. {
  2584. baseEvent = new InputEvent(DeltaStateEvent.Type, (int)eventSize, device.deviceId, time),
  2585. stateFormat = device.stateBlock.format,
  2586. stateOffset = control.m_StateBlock.byteOffset - device.m_StateBlock.byteOffset
  2587. };
  2588. var ptr = eventBuffer.stateEvent.stateData;
  2589. UnsafeUtility.MemCpy(ptr, UnsafeUtility.AddressOf(ref delta), deltaSize);
  2590. s_Manager.QueueEvent(ref eventBuffer.stateEvent);
  2591. }
  2592. /// <summary>
  2593. /// Queue a <see cref="DeviceConfigurationEvent"/> that signals that the configuration of the given device has changed
  2594. /// and that cached configuration will thus have to be refreshed.
  2595. /// </summary>
  2596. /// <param name="device">Device whose configuration has changed.</param>
  2597. /// <param name="time">Timestamp for the event. If not supplied, the current time will be used.</param>
  2598. /// <remarks>
  2599. /// All state of an input device that is not input or output state is considered its "configuration".
  2600. ///
  2601. /// A simple example is keyboard layouts. A <see cref="Keyboard"/> will typically have an associated
  2602. /// keyboard layout that dictates the function of each key and which can be changed by the user at the
  2603. /// system level. In the input system, the current keyboard layout can be queried via <see cref="Keyboard.keyboardLayout"/>.
  2604. /// When the layout changes at the system level, the input backend sends a configuration change event
  2605. /// to signal that the configuration of the keyboard has changed and that cached data may be outdated.
  2606. /// In response, <see cref="Keyboard"/> will flush out cached information such as the name of the keyboard
  2607. /// layout and display names (<see cref="InputControl.displayName"/>) of individual keys which causes them
  2608. /// to be fetched again from the backend the next time they are accessed.
  2609. /// </remarks>
  2610. /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
  2611. /// <exception cref="InvalidOperationException"><paramref name="device"/> has not been added
  2612. /// (<see cref="InputDevice.added"/>; <see cref="AddDevice(InputDevice)"/>) and thus cannot
  2613. /// receive events.</exception>
  2614. public static void QueueConfigChangeEvent(InputDevice device, double time = -1)
  2615. {
  2616. if (device == null)
  2617. throw new ArgumentNullException(nameof(device));
  2618. if (device.deviceId == InputDevice.InvalidDeviceId)
  2619. throw new InvalidOperationException("Device has not been added");
  2620. if (time < 0)
  2621. time = InputRuntime.s_Instance.currentTime;
  2622. else
  2623. time += InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup;
  2624. var inputEvent = DeviceConfigurationEvent.Create(device.deviceId, time);
  2625. s_Manager.QueueEvent(ref inputEvent);
  2626. }
  2627. /// <summary>
  2628. /// Queue a <see cref="TextEvent"/> on the given device.
  2629. /// </summary>
  2630. /// <param name="device">Device to queue the event on.</param>
  2631. /// <param name="character">Text character to input through the event.</param>
  2632. /// <param name="time">Optional event time stamp. If not supplied, the current time will be used.</param>
  2633. /// <remarks>
  2634. /// Text input is sent to devices character by character. This allows sending strings of arbitrary
  2635. /// length without necessary incurring GC overhead.
  2636. ///
  2637. /// For the event to have any effect on <paramref name="device"/>, the device must
  2638. /// implement <see cref="ITextInputReceiver"/>. It will see <see cref="ITextInputReceiver.OnTextInput"/>
  2639. /// being called when the event is processed.
  2640. /// </remarks>
  2641. /// <exception cref="ArgumentNullException"><paramref name="device"/> is null.</exception>
  2642. /// <exception cref="InvalidOperationException"><paramref name="device"/> is a device that has not been
  2643. /// added to the system.</exception>
  2644. /// <seealso cref="Keyboard.onTextInput"/>
  2645. public static void QueueTextEvent(InputDevice device, char character, double time = -1)
  2646. {
  2647. if (device == null)
  2648. throw new ArgumentNullException(nameof(device));
  2649. if (device.deviceId == InputDevice.InvalidDeviceId)
  2650. throw new InvalidOperationException("Device has not been added");
  2651. if (time < 0)
  2652. time = InputRuntime.s_Instance.currentTime;
  2653. else
  2654. time += InputRuntime.s_CurrentTimeOffsetToRealtimeSinceStartup;
  2655. var inputEvent = TextEvent.Create(device.deviceId, character, time);
  2656. s_Manager.QueueEvent(ref inputEvent);
  2657. }
  2658. /// <summary>
  2659. /// Run a single update of input state.
  2660. /// </summary>
  2661. /// <remarks>
  2662. /// Except in tests and when using <see cref="InputSettings.UpdateMode.ProcessEventsManually"/>, this method should not
  2663. /// normally be called. The input system will automatically update as part of the player loop as
  2664. /// determined by <see cref="InputSettings.updateMode"/>. Calling this method is equivalent to
  2665. /// inserting extra frames, i.e. it will advance the entire state of the input system by one complete
  2666. /// frame.
  2667. ///
  2668. /// When using <see cref="InputUpdateType.Manual"/>, this method MUST be called for input to update in the
  2669. /// player. Not calling the method as part of the player loop may result in excessive memory
  2670. /// consumption and/or potential loss of input.
  2671. ///
  2672. /// Each update will flush out buffered input events and cause them to be processed. This in turn
  2673. /// will update the state of input devices (<see cref="InputDevice"/>) and trigger actions (<see cref="InputAction"/>)
  2674. /// that monitor affected device state.
  2675. /// </remarks>
  2676. /// <seealso cref="InputUpdateType"/>
  2677. /// <seealso cref="InputSettings.updateMode"/>
  2678. public static void Update()
  2679. {
  2680. s_Manager.Update();
  2681. }
  2682. internal static void Update(InputUpdateType updateType)
  2683. {
  2684. if (updateType != InputUpdateType.None && (s_Manager.updateMask & updateType) == 0)
  2685. throw new InvalidOperationException(
  2686. $"'{updateType}' updates are not enabled; InputSystem.settings.updateMode is set to '{settings.updateMode}'");
  2687. s_Manager.Update(updateType);
  2688. }
  2689. /// <summary>
  2690. /// Event that is fired before the input system updates.
  2691. /// </summary>
  2692. /// <remarks>
  2693. /// The input system updates in sync with player loop and editor updates. Input updates
  2694. /// are run right before the respective script update. For example, an input update for
  2695. /// <see cref="InputUpdateType.Dynamic"/> is run before <c>MonoBehaviour.Update</c> methods
  2696. /// are executed.
  2697. ///
  2698. /// The update callback itself is triggered before the input system runs its own update and
  2699. /// before it flushes out its event queue. This means that events queued from a callback will
  2700. /// be fed right into the upcoming update.
  2701. /// </remarks>
  2702. /// <seealso cref="onAfterUpdate"/>
  2703. /// <seealso cref="Update"/>
  2704. public static event Action onBeforeUpdate
  2705. {
  2706. add
  2707. {
  2708. lock (s_Manager)
  2709. s_Manager.onBeforeUpdate += value;
  2710. }
  2711. remove
  2712. {
  2713. lock (s_Manager)
  2714. s_Manager.onBeforeUpdate -= value;
  2715. }
  2716. }
  2717. /// <summary>
  2718. /// Event that is fired after the input system has completed an update and processed all pending events.
  2719. /// </summary>
  2720. /// <seealso cref="onBeforeUpdate"/>
  2721. /// <seealso cref="Update"/>
  2722. public static event Action onAfterUpdate
  2723. {
  2724. add
  2725. {
  2726. lock (s_Manager)
  2727. s_Manager.onAfterUpdate += value;
  2728. }
  2729. remove
  2730. {
  2731. lock (s_Manager)
  2732. s_Manager.onAfterUpdate -= value;
  2733. }
  2734. }
  2735. #endregion
  2736. #region Settings
  2737. /// <summary>
  2738. /// The current configuration of the input system.
  2739. /// </summary>
  2740. /// <value>Global configuration object for the input system.</value>
  2741. /// <remarks>
  2742. /// The input system can be configured on a per-project basis. Settings can either be created and
  2743. /// installed on the fly or persisted as assets in the project.
  2744. /// </remarks>
  2745. /// <exception cref="ArgumentNullException">Value is null when setting the property.</exception>
  2746. public static InputSettings settings
  2747. {
  2748. get => s_Manager.settings;
  2749. set
  2750. {
  2751. if (value == null)
  2752. throw new ArgumentNullException(nameof(value));
  2753. if (s_Manager.m_Settings == value)
  2754. return;
  2755. // In the editor, we keep track of the settings asset through EditorBuildSettings.
  2756. #if UNITY_EDITOR
  2757. if (!string.IsNullOrEmpty(AssetDatabase.GetAssetPath(value)))
  2758. {
  2759. EditorBuildSettings.AddConfigObject(InputSettingsProvider.kEditorBuildSettingsConfigKey,
  2760. value, true);
  2761. }
  2762. #endif
  2763. s_Manager.settings = value;
  2764. }
  2765. }
  2766. /// <summary>
  2767. /// Event that is triggered if any of the properties in <see cref="settings"/> changes or if
  2768. /// <see cref="settings"/> is replaced entirely with a new <see cref="InputSettings"/> object.
  2769. /// </summary>
  2770. /// <seealso cref="settings"/>
  2771. /// <seealso cref="InputSettings"/>
  2772. public static event Action onSettingsChange
  2773. {
  2774. add => s_Manager.onSettingsChange += value;
  2775. remove => s_Manager.onSettingsChange -= value;
  2776. }
  2777. #if UNITY_EDITOR
  2778. /// <summary>
  2779. /// Callback that can be used to display a warning and draw additional custom Editor UI for bindings.
  2780. /// </summary>
  2781. /// <seealso cref="InputBinding"/>
  2782. /// <remarks>
  2783. /// This allows Users to control the behavior of the <see cref="InputActionAsset"/> Editor.
  2784. /// Specifically this controls whether a warning icon will appear next to a particular
  2785. /// <see cref="InputBinding"/> in the list and also draw custom UI content for it once
  2786. /// it is selected.
  2787. /// By default no callbacks exist and therefore no warnings or custom content will be shown.
  2788. /// A User interested in customizing this behavior is expected to provide a callback function here.
  2789. /// This callback function will receive the binding path to be inspected.
  2790. /// The callback is then expected to either return null to indicate no warning is to be displayed
  2791. /// for this binding path or a <see cref="System.Action"/> which contains the custom rendering function
  2792. /// to be shown in the Binding properties panel when a InputBinding has been selected.
  2793. /// Returning any <see cref="System.Action"/> will also display a small warning icon next to the
  2794. /// particular <see cref="InputBinding"/> in the list, regardless of the contents of that function.
  2795. /// </remarks>
  2796. ///
  2797. /// <example>
  2798. /// <code>
  2799. /// InputSystem.customBindingPathValidators += (string bindingPath) => {
  2800. /// // Mark <Gamepad> bindings with a warning
  2801. /// if (!bindingPath.StartsWith("<Gamepad>"))
  2802. /// return null;
  2803. ///
  2804. /// // Draw the warning information in the Binding Properties panel
  2805. /// return () =>
  2806. /// {
  2807. /// GUILayout.BeginVertical("GroupBox");
  2808. /// GUILayout.BeginHorizontal();
  2809. /// GUILayout.Box(EditorGUIUtility.FindTexture("console.warnicon.sml"));
  2810. /// GUILayout.Label(
  2811. /// "This binding is inactive because it refers to a disabled OpenXR interaction profile.",
  2812. /// EditorStyles.wordWrappedLabel);
  2813. /// GUILayout.EndHorizontal();
  2814. ///
  2815. /// GUILayout.Button("Manage Interaction Profiles");
  2816. /// GUILayout.EndVertical();
  2817. /// };
  2818. /// };
  2819. /// </code>
  2820. /// </example>
  2821. public static event CustomBindingPathValidator customBindingPathValidators
  2822. {
  2823. add => s_Manager.customBindingPathValidators += value;
  2824. remove => s_Manager.customBindingPathValidators -= value;
  2825. }
  2826. /// <summary>
  2827. /// Invokes any custom UI rendering code for this Binding Path in the editor.
  2828. /// </summary>
  2829. /// <seealso cref="customBindingPathValidators"/>
  2830. /// <remarks>
  2831. /// This is called internally by the <see cref="InputActionAsset"/> Editor while displaying
  2832. /// the properties for a <see cref="InputBinding"/>.
  2833. /// This is not intended to be called directly.
  2834. /// Please use <see cref="customBindingPathValidators"/> instead.
  2835. /// </remarks>
  2836. internal static void OnDrawCustomWarningForBindingPath(string bindingPath)
  2837. {
  2838. s_Manager.OnDrawCustomWarningForBindingPath(bindingPath);
  2839. }
  2840. /// <summary>
  2841. /// Determines if any warning icon is to be displayed for this Binding Path in the editor.
  2842. /// </summary>
  2843. /// <seealso cref="customBindingPathValidators"/>
  2844. /// <remarks>
  2845. /// This is called internally by the <see cref="InputActionAsset"/> Editor while displaying
  2846. /// the list of each <see cref="InputBinding"/>.
  2847. /// This is not intended to be called directly.
  2848. /// Please use <see cref="customBindingPathValidators"/> instead.
  2849. /// </remarks>
  2850. internal static bool ShouldDrawWarningIconForBinding(string bindingPath)
  2851. {
  2852. return s_Manager.ShouldDrawWarningIconForBinding(bindingPath);
  2853. }
  2854. #endif
  2855. #endregion
  2856. #region Actions
  2857. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  2858. // EnteredEditMode Occurs during the next update of the Editor application if it is in edit mode and was previously in play mode.
  2859. // ExitingEditMode Occurs when exiting edit mode, before the Editor is in play mode.
  2860. // EnteredPlayMode Occurs during the next update of the Editor application if it is in play mode and was previously in edit mode.
  2861. // ExitingPlayMode Occurs when exiting play mode, before the Editor is in edit mode.
  2862. //
  2863. // Using the EnteredEditMode / EnteredPlayMode states to transition the actions' enabled
  2864. // state ensures that the they are active in all of these MonoBehavior methods:
  2865. //
  2866. // Awake() / Start() / OnEnable() / OnDisable() / OnDestroy()
  2867. //
  2868. private static void EnableActions()
  2869. {
  2870. #if UNITY_EDITOR
  2871. // Abort if not in play-mode in editor
  2872. if (!EditorApplication.isPlayingOrWillChangePlaymode)
  2873. return;
  2874. #endif // UNITY_EDITOR
  2875. if (actions == null)
  2876. return;
  2877. actions.Enable();
  2878. }
  2879. private static void DisableActions(bool triggerSetupChanged = false)
  2880. {
  2881. // Make sure project wide input actions are disabled
  2882. var projectWideActions = actions;
  2883. if (projectWideActions == null)
  2884. return;
  2885. projectWideActions.Disable();
  2886. if (triggerSetupChanged)
  2887. projectWideActions.OnSetupChanged();
  2888. }
  2889. /// <summary>
  2890. /// An input action asset (see <see cref="InputActionAsset"/>) which is always available if
  2891. /// assigned in Input System Package settings in Edit, Project Settings, Input System Package in editor.
  2892. /// </summary>
  2893. /// <remarks>
  2894. /// Project-wide actions may only be assigned in Edit Mode and any attempt to change this property
  2895. /// in Play Mode will result in an <c>System.Exception</c> being thrown.
  2896. /// A default set of actions and action maps are installed and enabled by default on every project
  2897. /// that enables Project-wide Input Actions by assigning a project-wide asset in Project Settings.
  2898. /// These actions and their bindings may be modified in the Project Settings.
  2899. ///
  2900. /// All actions in the associated <c>InputActionAsset</c> will be automatically enabled when entering
  2901. /// Play Mode and automatically disabled when exiting Play Mode.
  2902. /// The asset associated with this property will be included in a Player build as a preloaded asset.
  2903. ///
  2904. /// Note that attempting to assign a non-persisted <c>InputActionAsset</c> to this property will result in
  2905. /// <c>ArgumentException</c> being thrown.
  2906. /// </remarks>
  2907. /// <seealso cref="InputActionAsset"/>
  2908. /// <seealso cref="InputActionMap"/>
  2909. /// <seealso cref="InputAction"/>
  2910. /// <example>
  2911. /// <code>
  2912. /// public class MyScript : MonoBehaviour
  2913. /// {
  2914. /// InputAction move;
  2915. /// InputAction jump;
  2916. ///
  2917. /// void Start()
  2918. /// {
  2919. /// // Get InputAction references from Project-wide input actions.
  2920. /// if (InputSystem.actions)
  2921. /// {
  2922. /// move = InputSystem.actions.FindAction("Player/Move");
  2923. /// jump = InputSystem.actions.FindAction("Player/Jump");
  2924. /// }
  2925. /// }
  2926. /// }
  2927. /// </code>
  2928. /// </example>
  2929. public static InputActionAsset actions
  2930. {
  2931. get => s_Manager?.actions;
  2932. set
  2933. {
  2934. // Prevent this property from being assigned in play-mode.
  2935. if (Application.isPlaying)
  2936. throw new Exception($"Attempted to set property InputSystem.actions during Play-mode which is not supported. Assigning this property is only allowed in Edit-mode.");
  2937. // Note that we use reference equality to determine if object changed or not.
  2938. // This allows us to change the associated value even if changed or destroyed.
  2939. var current = s_Manager.actions;
  2940. if (ReferenceEquals(current, value))
  2941. return;
  2942. var valueIsNotNull = value != null;
  2943. #if UNITY_EDITOR
  2944. // Do not allow assigning non-persistent assets (pure in-memory objects)
  2945. if (valueIsNotNull && !EditorUtility.IsPersistent(value))
  2946. throw new ArgumentException($"Assigning a non-persistent {nameof(InputActionAsset)} to this property is not allowed. The assigned asset need to be persisted on disc inside the /Assets folder.");
  2947. // Track reference to enable including it in built Players, note that it will discard any non-persisted
  2948. // object reference
  2949. ProjectWideActionsBuildProvider.actionsToIncludeInPlayerBuild = value;
  2950. #endif // UNITY_EDITOR
  2951. // Update underlying value
  2952. s_Manager.actions = value;
  2953. // Note that we do not enable/disable any actions until play-mode
  2954. }
  2955. }
  2956. /// <summary>
  2957. /// Event that is triggered if the instance assigned to property <see cref="actions"/> changes.
  2958. /// </summary>
  2959. /// <remarks>
  2960. /// Note that any event handlers registered to this event will only receive callbacks in Edit mode
  2961. /// since assigning <c>InputSystem.actions</c> is not possible in Play mode.
  2962. /// </remarks>
  2963. /// <seealso cref="actions"/>
  2964. /// <seealso cref="InputActionAsset"/>
  2965. public static event Action onActionsChange
  2966. {
  2967. add => s_Manager.onActionsChange += value;
  2968. remove => s_Manager.onActionsChange -= value;
  2969. }
  2970. #endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  2971. /// <summary>
  2972. /// Event that is signalled when the state of enabled actions in the system changes or
  2973. /// when actions are triggered.
  2974. /// </summary>
  2975. /// <remarks>
  2976. /// The object received by the callback is either an <see cref="InputAction"/>,
  2977. /// <see cref="InputActionMap"/>, or <see cref="InputActionAsset"/> depending on whether the
  2978. /// <see cref="InputActionChange"/> affects a single action, an entire action map, or an
  2979. /// entire action asset.
  2980. ///
  2981. /// For <see cref="InputActionChange.BoundControlsAboutToChange"/> and <see cref="InputActionChange.BoundControlsChanged"/>,
  2982. /// the given object is an <see cref="InputAction"/> if the action is not part of an action map,
  2983. /// an <see cref="InputActionMap"/> if the actions are part of a map but not part of an asset, and an
  2984. /// <see cref="InputActionAsset"/> if the actions are part of an asset. In other words, the notification is
  2985. /// sent for the topmost object in the hierarchy.
  2986. /// </remarks>
  2987. /// <example>
  2988. /// <code>
  2989. /// InputSystem.onActionChange +=
  2990. /// (obj, change) =>
  2991. /// {
  2992. /// if (change == InputActionChange.ActionPerformed)
  2993. /// {
  2994. /// var action = (InputAction)obj;
  2995. /// var control = action.activeControl;
  2996. /// //...
  2997. /// }
  2998. /// else if (change == InputActionChange.ActionMapEnabled)
  2999. /// {
  3000. /// var actionMap = (InputActionMap)obj;
  3001. /// //...
  3002. /// }
  3003. /// else if (change == InputActionChange.BoundControlsChanged)
  3004. /// {
  3005. /// // This is one way to deal with the fact that obj may be an InputAction
  3006. /// // InputActionMap, or InputActionAsset and may be part of an InputActionAsset or not.
  3007. /// var action = obj as InputAction;
  3008. /// var actionMap = action?.actionMap ?? obj as InputActionMap;
  3009. /// var actionAsset = actionMap?.asset ?? obj as InputActionAsset;
  3010. ///
  3011. /// // Note that if bound controls are changed on any map in an asset, there *will*
  3012. /// // be a BoundControlsChanged notification for the entire asset.
  3013. ///
  3014. /// //...
  3015. /// }
  3016. /// };
  3017. /// </code>
  3018. /// </example>
  3019. /// <seealso cref="InputAction.controls"/>
  3020. public static event Action<object, InputActionChange> onActionChange
  3021. {
  3022. add
  3023. {
  3024. if (value == null)
  3025. throw new ArgumentNullException(nameof(value));
  3026. InputActionState.s_GlobalState.onActionChange.AddCallback(value);
  3027. }
  3028. remove
  3029. {
  3030. if (value == null)
  3031. throw new ArgumentNullException(nameof(value));
  3032. InputActionState.s_GlobalState.onActionChange.RemoveCallback(value);
  3033. }
  3034. }
  3035. /// <summary>
  3036. /// Register a new type of interaction with the system.
  3037. /// </summary>
  3038. /// <param name="type">Type that implements the interaction. Must support <see cref="InputInteraction"/>.</param>
  3039. /// <param name="name">Name to register the interaction with. This is used in bindings to refer to the interaction
  3040. /// (e.g. an interactions called "Tap" can be added to a binding by listing it in its <see cref="InputBinding.interactions"/>
  3041. /// property). If no name is supplied, the short name of <paramref name="type"/> is used (with "Interaction" clipped off
  3042. /// the name if the type name ends in that).</param>
  3043. /// <example>
  3044. /// <code>
  3045. /// // Interaction that is performed when control resets to default state.
  3046. /// public class ResetInteraction : InputInteraction
  3047. /// {
  3048. /// public void Process(ref InputInteractionContext context)
  3049. /// {
  3050. /// if (context.isWaiting &amp;&amp; !context.controlHasDefaultValue)
  3051. /// context.Started();
  3052. /// else if (context.isStarted &amp;&amp; context.controlHasDefaultValue)
  3053. /// context.Performed();
  3054. /// }
  3055. /// }
  3056. ///
  3057. /// // Make interaction globally available on bindings.
  3058. /// // "Interaction" suffix in type name will get dropped automatically.
  3059. /// InputSystem.RegisterInteraction(typeof(ResetInteraction));
  3060. ///
  3061. /// // Set up action with binding that has the 'reset' interaction applied to it.
  3062. /// var action = new InputAction(binding: "/&lt;Gamepad>/buttonSouth", interactions: "reset");
  3063. /// </code>
  3064. /// </example>
  3065. /// <seealso cref="IInputInteraction"/>
  3066. /// <seealso cref="RegisterInteraction{T}"/>
  3067. /// <seealso cref="TryGetInteraction"/>
  3068. /// <seealso cref="ListInteractions"/>
  3069. public static void RegisterInteraction(Type type, string name = null)
  3070. {
  3071. if (type == null)
  3072. throw new ArgumentNullException(nameof(type));
  3073. if (string.IsNullOrEmpty(name))
  3074. {
  3075. name = type.Name;
  3076. if (name.EndsWith("Interaction"))
  3077. name = name.Substring(0, name.Length - "Interaction".Length);
  3078. }
  3079. s_Manager.interactions.AddTypeRegistration(name, type);
  3080. }
  3081. /// <summary>
  3082. /// Register a new type of interaction with the system.
  3083. /// </summary>
  3084. /// <typeparam name="T">Type that implements the interaction. Must support <see cref="InputInteraction"/>.</typeparam>
  3085. /// <param name="name">Name to register the interaction with. This is used in bindings to refer to the interaction
  3086. /// (e.g. an interactions called "Tap" can be added to a binding by listing it in its <see cref="InputBinding.interactions"/>
  3087. /// property). If no name is supplied, the short name of <typeparamref name="T"/> is used (with "Interaction" clipped off
  3088. /// the name if the type name ends in that).</param>
  3089. /// <seealso cref="IInputInteraction"/>
  3090. /// <seealso cref="RegisterInteraction(Type, string)"/>
  3091. /// <seealso cref="TryGetInteraction"/>
  3092. /// <seealso cref="ListInteractions"/>
  3093. public static void RegisterInteraction<T>(string name = null)
  3094. {
  3095. RegisterInteraction(typeof(T), name);
  3096. }
  3097. ////REVIEW: can we move the getters and listers somewhere else? maybe `interactions` and `processors` properties and such?
  3098. /// <summary>
  3099. /// Search for a registered interaction type with the given name.
  3100. /// </summary>
  3101. /// <param name="name">Name of the registered interaction to search for.</param>
  3102. /// <returns>The type of the interaction, if one was previously registered with the give name, otherwise null.</returns>
  3103. /// <seealso cref="IInputInteraction"/>
  3104. /// <seealso cref="RegisterInteraction"/>
  3105. /// <seealso cref="ListInteractions"/>
  3106. public static Type TryGetInteraction(string name)
  3107. {
  3108. if (string.IsNullOrEmpty(name))
  3109. throw new ArgumentNullException(nameof(name));
  3110. return s_Manager.interactions.LookupTypeRegistration(name);
  3111. }
  3112. /// <summary>
  3113. /// Gets the names of of all currently registered interactions.
  3114. /// </summary>
  3115. /// <returns>A list of currently registered interaction names.</returns>
  3116. /// <seealso cref="IInputInteraction"/>
  3117. /// <seealso cref="RegisterInteraction"/>
  3118. /// <seealso cref="TryGetInteraction"/>
  3119. public static IEnumerable<string> ListInteractions()
  3120. {
  3121. return s_Manager.interactions.names;
  3122. }
  3123. /// <summary>
  3124. /// Register a new type of binding composite with the system.
  3125. /// </summary>
  3126. /// <param name="type">Type that implements the binding composite. Must support <see cref="InputBindingComposite"/>.</param>
  3127. /// <param name="name">Name to register the binding composite with. This is used in bindings to refer to the composite.</param>
  3128. /// <seealso cref="InputBindingComposite"/>
  3129. /// <seealso cref="RegisterBindingComposite{T}"/>
  3130. /// <seealso cref="TryGetBindingComposite"/>
  3131. public static void RegisterBindingComposite(Type type, string name)
  3132. {
  3133. if (type == null)
  3134. throw new ArgumentNullException(nameof(type));
  3135. if (string.IsNullOrEmpty(name))
  3136. {
  3137. name = type.Name;
  3138. if (name.EndsWith("Composite"))
  3139. name = name.Substring(0, name.Length - "Composite".Length);
  3140. }
  3141. s_Manager.composites.AddTypeRegistration(name, type);
  3142. }
  3143. /// <summary>
  3144. /// Register a new type of binding composite with the system.
  3145. /// </summary>
  3146. /// <typeparam name="T">Type that implements the binding composite. Must support <see cref="InputBindingComposite"/>.</typeparam>
  3147. /// <param name="name">Name to register the binding composite with. This is used in bindings to refer to the composite.</param>
  3148. /// <seealso cref="InputBindingComposite"/>
  3149. /// <seealso cref="RegisterBindingComposite(Type, string)"/>
  3150. /// <seealso cref="TryGetBindingComposite"/>
  3151. public static void RegisterBindingComposite<T>(string name = null)
  3152. {
  3153. RegisterBindingComposite(typeof(T), name);
  3154. }
  3155. /// <summary>
  3156. /// Search for a registered binding composite type with the given name.
  3157. /// </summary>
  3158. /// <param name="name">Name of the registered binding composite to search for.</param>
  3159. /// <returns>The type of the binding composite, if one was previously registered with the give name, otherwise null.</returns>
  3160. /// <seealso cref="InputBindingComposite"/>
  3161. /// <seealso cref="RegisterBindingComposite"/>
  3162. public static Type TryGetBindingComposite(string name)
  3163. {
  3164. if (string.IsNullOrEmpty(name))
  3165. throw new ArgumentNullException(nameof(name));
  3166. return s_Manager.composites.LookupTypeRegistration(name);
  3167. }
  3168. /// <summary>
  3169. /// Disable all actions (and implicitly all action sets) that are currently enabled.
  3170. /// </summary>
  3171. /// <seealso cref="ListEnabledActions()"/>
  3172. /// <seealso cref="InputAction.Disable"/>
  3173. public static void DisableAllEnabledActions()
  3174. {
  3175. InputActionState.DisableAllActions();
  3176. }
  3177. /// <summary>
  3178. /// Return a list of all the actions that are currently enabled in the system.
  3179. /// </summary>
  3180. /// <returns>A new list instance containing all currently enabled actions.</returns>
  3181. /// <remarks>
  3182. /// To avoid allocations, use <see cref="ListEnabledActions(List{UnityEngine.InputSystem.InputAction})"/>.
  3183. /// </remarks>
  3184. /// <seealso cref="InputAction.enabled"/>
  3185. public static List<InputAction> ListEnabledActions()
  3186. {
  3187. var result = new List<InputAction>();
  3188. ListEnabledActions(result);
  3189. return result;
  3190. }
  3191. /// <summary>
  3192. /// Add all actions that are currently enabled in the system to the given list.
  3193. /// </summary>
  3194. /// <param name="actions">List to add actions to.</param>
  3195. /// <returns>The number of actions added to the list.</returns>
  3196. /// <exception cref="ArgumentNullException"><paramref name="actions"/> is null.</exception>
  3197. /// <remarks>
  3198. /// If the capacity of the given list is large enough, this method will not allocate memory.
  3199. /// </remarks>
  3200. public static int ListEnabledActions(List<InputAction> actions)
  3201. {
  3202. if (actions == null)
  3203. throw new ArgumentNullException(nameof(actions));
  3204. return InputActionState.FindAllEnabledActions(actions);
  3205. }
  3206. #endregion
  3207. #region Remoting
  3208. /// <summary>
  3209. /// The local InputRemoting instance which can mirror local input to a remote
  3210. /// input system or can make input in a remote system available locally.
  3211. /// </summary>
  3212. /// <remarks>
  3213. /// In the editor, this is always initialized. In players, this will be null
  3214. /// if remoting is disabled (which it is by default in release players).
  3215. /// </remarks>
  3216. public static InputRemoting remoting => s_Remote;
  3217. #endregion
  3218. /// <summary>
  3219. /// The current version of the input system package.
  3220. /// </summary>
  3221. /// <value>Current version of the input system.</value>
  3222. public static Version version => new Version(kAssemblyVersion);
  3223. /// <summary>
  3224. /// Property for internal use that allows setting the player to run in the background.
  3225. /// </summary>
  3226. /// <remarks>
  3227. /// Some platforms don't care about <see cref="Application.runInBackground"/> and for those we need to
  3228. /// enable it manually through this propriety.
  3229. /// </remarks>
  3230. /// <param name="value">The boolean value to set to <see cref="NativeInputRuntime.runInBackground"/></param>
  3231. public static bool runInBackground
  3232. {
  3233. get => s_Manager.m_Runtime.runInBackground;
  3234. set => s_Manager.m_Runtime.runInBackground = value;
  3235. }
  3236. ////REVIEW: restrict metrics to editor and development builds?
  3237. /// <summary>
  3238. /// Get various up-to-date metrics about the input system.
  3239. /// </summary>
  3240. /// <value>Up-to-date metrics on input system activity.</value>
  3241. public static InputMetrics metrics => s_Manager.metrics;
  3242. internal static InputManager s_Manager;
  3243. internal static InputRemoting s_Remote;
  3244. #if DEVELOPMENT_BUILD || UNITY_EDITOR
  3245. internal static RemoteInputPlayerConnection s_RemoteConnection;
  3246. private static void SetUpRemoting()
  3247. {
  3248. Debug.Assert(s_Manager != null);
  3249. #if UNITY_EDITOR
  3250. s_Remote = new InputRemoting(s_Manager);
  3251. // NOTE: We use delayCall as our initial startup will run in editor initialization before
  3252. // PlayerConnection is itself ready. If we call Bind() directly here, we won't
  3253. // see any errors but the callbacks we register for will not trigger.
  3254. EditorApplication.delayCall += SetUpRemotingInternal;
  3255. #else
  3256. s_Remote = new InputRemoting(s_Manager);
  3257. SetUpRemotingInternal();
  3258. #endif
  3259. }
  3260. private static void SetUpRemotingInternal()
  3261. {
  3262. if (s_RemoteConnection == null)
  3263. {
  3264. #if UNITY_EDITOR
  3265. s_RemoteConnection = RemoteInputPlayerConnection.instance;
  3266. s_RemoteConnection.Bind(EditorConnection.instance, false);
  3267. #else
  3268. s_RemoteConnection = ScriptableObject.CreateInstance<RemoteInputPlayerConnection>();
  3269. s_RemoteConnection.Bind(PlayerConnection.instance, PlayerConnection.instance.isConnected);
  3270. #endif
  3271. }
  3272. s_Remote.Subscribe(s_RemoteConnection); // Feed messages from players into editor.
  3273. s_RemoteConnection.Subscribe(s_Remote); // Feed messages from editor into players.
  3274. }
  3275. #if !UNITY_EDITOR
  3276. private static bool ShouldEnableRemoting()
  3277. {
  3278. #if UNITY_INCLUDE_TESTS
  3279. var isRunningTests = true;
  3280. #else
  3281. var isRunningTests = false;
  3282. #endif
  3283. if (isRunningTests)
  3284. return false; // Don't remote while running tests.
  3285. return true;
  3286. }
  3287. #endif
  3288. #endif // DEVELOPMENT_BUILD || UNITY_EDITOR
  3289. // The rest here is internal stuff to manage singletons, survive domain reloads,
  3290. // and to support the reset ability for tests.
  3291. static InputSystem()
  3292. {
  3293. #if UNITY_EDITOR
  3294. InitializeInEditor();
  3295. #else
  3296. InitializeInPlayer();
  3297. #endif
  3298. }
  3299. ////FIXME: Unity is not calling this method if it's inside an #if block that is not
  3300. //// visible to the editor; that shouldn't be the case
  3301. [RuntimeInitializeOnLoadMethod(loadType: RuntimeInitializeLoadType.SubsystemRegistration)]
  3302. private static void RunInitializeInPlayer()
  3303. {
  3304. // We're using this method just to make sure the class constructor is called
  3305. // so we don't need any code in here. When the engine calls this method, the
  3306. // class constructor will be run if it hasn't been run already.
  3307. // IL2CPP has a bug that causes the class constructor to not be run when
  3308. // the RuntimeInitializeOnLoadMethod is invoked. So we need an explicit check
  3309. // here until that is fixed (case 1014293).
  3310. #if !UNITY_EDITOR
  3311. if (s_Manager == null)
  3312. InitializeInPlayer();
  3313. #endif
  3314. }
  3315. // Initialization is triggered by accessing InputSystem. Some parts (like InputActions)
  3316. // do not rely on InputSystem and thus can be accessed without tapping InputSystem.
  3317. // This method will explicitly make sure we trigger initialization.
  3318. internal static void EnsureInitialized()
  3319. {
  3320. }
  3321. #if UNITY_EDITOR
  3322. internal static InputSystemObject s_SystemObject;
  3323. internal static void InitializeInEditor(IInputRuntime runtime = null)
  3324. {
  3325. Profiler.BeginSample("InputSystem.InitializeInEditor");
  3326. Reset(runtime: runtime);
  3327. var existingSystemObjects = Resources.FindObjectsOfTypeAll<InputSystemObject>();
  3328. if (existingSystemObjects != null && existingSystemObjects.Length > 0)
  3329. {
  3330. ////FIXME: does not preserve action map state
  3331. // We're coming back out of a domain reload. We're restoring part of the
  3332. // InputManager state here but we're still waiting from layout registrations
  3333. // that happen during domain initialization.
  3334. s_SystemObject = existingSystemObjects[0];
  3335. s_Manager.RestoreStateWithoutDevices(s_SystemObject.systemState.managerState);
  3336. InputDebuggerWindow.ReviveAfterDomainReload();
  3337. // Restore remoting state.
  3338. s_RemoteConnection = s_SystemObject.systemState.remoteConnection;
  3339. SetUpRemoting();
  3340. s_Remote.RestoreState(s_SystemObject.systemState.remotingState, s_Manager);
  3341. // Get manager to restore devices on first input update. By that time we
  3342. // should have all (possibly updated) layout information in place.
  3343. s_Manager.m_SavedDeviceStates = s_SystemObject.systemState.managerState.devices;
  3344. s_Manager.m_SavedAvailableDevices = s_SystemObject.systemState.managerState.availableDevices;
  3345. // Restore editor settings.
  3346. InputEditorUserSettings.s_Settings = s_SystemObject.systemState.userSettings;
  3347. // Get rid of saved state.
  3348. s_SystemObject.systemState = new State();
  3349. }
  3350. else
  3351. {
  3352. s_SystemObject = ScriptableObject.CreateInstance<InputSystemObject>();
  3353. s_SystemObject.hideFlags = HideFlags.HideAndDontSave;
  3354. // See if we have a remembered settings object.
  3355. if (EditorBuildSettings.TryGetConfigObject(InputSettingsProvider.kEditorBuildSettingsConfigKey,
  3356. out InputSettings settingsAsset))
  3357. {
  3358. if (s_Manager.m_Settings.hideFlags == HideFlags.HideAndDontSave)
  3359. ScriptableObject.DestroyImmediate(s_Manager.m_Settings);
  3360. s_Manager.m_Settings = settingsAsset;
  3361. s_Manager.ApplySettings();
  3362. }
  3363. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  3364. // See if we have a saved actions object
  3365. var savedActions = ProjectWideActionsBuildProvider.actionsToIncludeInPlayerBuild;
  3366. if (savedActions != null)
  3367. s_Manager.actions = savedActions;
  3368. #endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  3369. InputEditorUserSettings.Load();
  3370. SetUpRemoting();
  3371. }
  3372. Debug.Assert(settings != null);
  3373. #if UNITY_EDITOR
  3374. Debug.Assert(EditorUtility.InstanceIDToObject(settings.GetInstanceID()) != null,
  3375. "InputSettings has lost its native object");
  3376. #endif
  3377. // If native backends for new input system aren't enabled, ask user whether we should
  3378. // enable them (requires restart). We only ask once per session and don't ask when
  3379. // running in batch mode.
  3380. if (!s_SystemObject.newInputBackendsCheckedAsEnabled &&
  3381. !EditorPlayerSettingHelpers.newSystemBackendsEnabled &&
  3382. !s_Manager.m_Runtime.isInBatchMode)
  3383. {
  3384. const string dialogText = "This project is using the new input system package but the native platform backends for the new input system are not enabled in the player settings. " +
  3385. "This means that no input from native devices will come through." +
  3386. "\n\nDo you want to enable the backends? Doing so will *RESTART* the editor.";
  3387. if (EditorUtility.DisplayDialog("Warning", dialogText, "Yes", "No"))
  3388. {
  3389. EditorPlayerSettingHelpers.newSystemBackendsEnabled = true;
  3390. EditorHelpers.RestartEditorAndRecompileScripts();
  3391. }
  3392. }
  3393. s_SystemObject.newInputBackendsCheckedAsEnabled = true;
  3394. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  3395. // Make sure project wide input actions are enabled.
  3396. // Note that this will always fail if entering play-mode within editor since not yet in play-mode.
  3397. EnableActions();
  3398. #endif
  3399. RunInitialUpdate();
  3400. Profiler.EndSample();
  3401. }
  3402. internal static void OnPlayModeChange(PlayModeStateChange change)
  3403. {
  3404. ////REVIEW: should we pause haptics when play mode is paused and stop haptics when play mode is exited?
  3405. switch (change)
  3406. {
  3407. case PlayModeStateChange.ExitingEditMode:
  3408. s_SystemObject.settings = JsonUtility.ToJson(settings);
  3409. s_SystemObject.exitEditModeTime = InputRuntime.s_Instance.currentTime;
  3410. s_SystemObject.enterPlayModeTime = 0;
  3411. // InputSystem.actions is not setup yet
  3412. break;
  3413. case PlayModeStateChange.EnteredPlayMode:
  3414. s_SystemObject.enterPlayModeTime = InputRuntime.s_Instance.currentTime;
  3415. s_Manager.SyncAllDevicesAfterEnteringPlayMode();
  3416. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  3417. EnableActions();
  3418. #endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  3419. break;
  3420. case PlayModeStateChange.ExitingPlayMode:
  3421. s_Manager.LeavePlayMode();
  3422. break;
  3423. ////TODO: also nuke all callbacks installed on InputActions and InputActionMaps
  3424. ////REVIEW: is there any other cleanup work we want to before? should we automatically nuke
  3425. //// InputDevices that have been created with AddDevice<> during play mode?
  3426. case PlayModeStateChange.EnteredEditMode:
  3427. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  3428. DisableActions(false);
  3429. #endif
  3430. // Nuke all InputUsers.
  3431. InputUser.ResetGlobals();
  3432. // Nuke all InputActionMapStates. Releases their unmanaged memory.
  3433. InputActionState.DestroyAllActionMapStates();
  3434. // Restore settings.
  3435. if (!string.IsNullOrEmpty(s_SystemObject.settings))
  3436. {
  3437. JsonUtility.FromJsonOverwrite(s_SystemObject.settings, settings);
  3438. s_SystemObject.settings = null;
  3439. settings.OnChange();
  3440. }
  3441. // reload input action assets marked as dirty from disk
  3442. if (s_TrackedDirtyAssets == null)
  3443. return;
  3444. foreach (var assetGuid in s_TrackedDirtyAssets)
  3445. {
  3446. var assetPath = AssetDatabase.GUIDToAssetPath(assetGuid);
  3447. if (string.IsNullOrEmpty(assetPath))
  3448. continue;
  3449. AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
  3450. }
  3451. s_TrackedDirtyAssets.Clear();
  3452. break;
  3453. }
  3454. }
  3455. private static void OnProjectChange()
  3456. {
  3457. ////TODO: use dirty count to find whether settings have actually changed
  3458. // May have added, removed, moved, or renamed settings asset. Force a refresh
  3459. // of the UI.
  3460. InputSettingsProvider.ForceReload();
  3461. // Also, if the asset holding our current settings got deleted, switch back to a
  3462. // temporary settings object.
  3463. // NOTE: We access m_Settings directly here to make sure we're not running into asserts
  3464. // from the settings getter checking it has a valid object.
  3465. if (EditorUtility.InstanceIDToObject(s_Manager.m_Settings.GetInstanceID()) == null)
  3466. {
  3467. var newSettings = ScriptableObject.CreateInstance<InputSettings>();
  3468. newSettings.hideFlags = HideFlags.HideAndDontSave;
  3469. settings = newSettings;
  3470. }
  3471. }
  3472. private static HashSet<string> s_TrackedDirtyAssets;
  3473. /// <summary>
  3474. /// Keep track of InputActionAsset assets that you want to re-load on exiting Play mode. This is useful because
  3475. /// some user actions, such as adding a new input binding at runtime, change the in-memory representation of the
  3476. /// input action asset and those changes survive when exiting Play mode. If you re-open an Input
  3477. /// Action Asset in the Editor that has been changed this way, you see the new bindings that have been added
  3478. /// during Play mode which you might not typically want to happen.
  3479. ///
  3480. /// You can avoid this by force re-loading from disk any asset that has been marked as dirty.
  3481. /// </summary>
  3482. /// <param name="asset"></param>
  3483. internal static void TrackDirtyInputActionAsset(InputActionAsset asset)
  3484. {
  3485. if (s_TrackedDirtyAssets == null)
  3486. s_TrackedDirtyAssets = new HashSet<string>();
  3487. if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out string assetGuid, out long _) == false)
  3488. return;
  3489. s_TrackedDirtyAssets.Add(assetGuid);
  3490. }
  3491. #else
  3492. private static void InitializeInPlayer(IInputRuntime runtime = null, InputSettings settings = null)
  3493. {
  3494. if (settings == null)
  3495. settings = Resources.FindObjectsOfTypeAll<InputSettings>().FirstOrDefault() ?? ScriptableObject.CreateInstance<InputSettings>();
  3496. // No domain reloads in the player so we don't need to look for existing
  3497. // instances.
  3498. s_Manager = new InputManager();
  3499. s_Manager.Initialize(runtime ?? NativeInputRuntime.instance, settings);
  3500. #if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION
  3501. PerformDefaultPluginInitialization();
  3502. #endif
  3503. // Automatically enable remoting in development players.
  3504. #if DEVELOPMENT_BUILD
  3505. if (ShouldEnableRemoting())
  3506. SetUpRemoting();
  3507. #endif
  3508. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS // && !UNITY_INCLUDE_TESTS
  3509. // This is the point where we initialise project-wide actions for the Player
  3510. EnableActions();
  3511. #endif
  3512. }
  3513. #endif // UNITY_EDITOR
  3514. [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
  3515. private static void RunInitialUpdate()
  3516. {
  3517. // Request an initial Update so that user methods such as Start and Awake
  3518. // can access the input devices.
  3519. //
  3520. // NOTE: We use InputUpdateType.None here to run a "null" update. InputManager.OnBeforeUpdate()
  3521. // and InputManager.OnUpdate() will both early out when comparing this to their update
  3522. // mask but will still restore devices. This means we're not actually processing input,
  3523. // but we will force the runtime to push its devices.
  3524. Update(InputUpdateType.None);
  3525. }
  3526. #if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION
  3527. private static void PerformDefaultPluginInitialization()
  3528. {
  3529. UISupport.Initialize();
  3530. #if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WSA || UNITY_ANDROID || UNITY_IOS || UNITY_TVOS || UNITY_VISIONOS
  3531. XInputSupport.Initialize();
  3532. #endif
  3533. #if UNITY_EDITOR || UNITY_STANDALONE || UNITY_PS4 || UNITY_PS5 || UNITY_WSA || UNITY_ANDROID || UNITY_IOS || UNITY_TVOS || UNITY_VISIONOS
  3534. DualShockSupport.Initialize();
  3535. #endif
  3536. #if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WSA
  3537. HIDSupport.Initialize();
  3538. #endif
  3539. #if UNITY_EDITOR || UNITY_ANDROID
  3540. Android.AndroidSupport.Initialize();
  3541. #endif
  3542. #if UNITY_EDITOR || UNITY_IOS || UNITY_TVOS || UNITY_VISIONOS
  3543. iOS.iOSSupport.Initialize();
  3544. #endif
  3545. #if UNITY_EDITOR || UNITY_STANDALONE_OSX
  3546. OSX.OSXSupport.Initialize();
  3547. #endif
  3548. #if UNITY_EDITOR || UNITY_WEBGL
  3549. WebGL.WebGLSupport.Initialize();
  3550. #endif
  3551. #if UNITY_EDITOR || UNITY_STANDALONE_OSX || UNITY_STANDALONE_WIN || UNITY_WSA
  3552. Switch.SwitchSupportHID.Initialize();
  3553. #endif
  3554. #if UNITY_INPUT_SYSTEM_ENABLE_XR && (ENABLE_VR || UNITY_GAMECORE) && !UNITY_FORCE_INPUTSYSTEM_XR_OFF
  3555. XR.XRSupport.Initialize();
  3556. #endif
  3557. #if UNITY_EDITOR || UNITY_STANDALONE_LINUX
  3558. Linux.LinuxSupport.Initialize();
  3559. #endif
  3560. #if UNITY_EDITOR || UNITY_ANDROID || UNITY_IOS || UNITY_TVOS || UNITY_WSA || UNITY_VISIONOS
  3561. OnScreen.OnScreenSupport.Initialize();
  3562. #endif
  3563. #if (UNITY_EDITOR || UNITY_STANDALONE) && UNITY_ENABLE_STEAM_CONTROLLER_SUPPORT
  3564. Steam.SteamSupport.Initialize();
  3565. #endif
  3566. #if UNITY_EDITOR
  3567. UnityRemoteSupport.Initialize();
  3568. #endif
  3569. }
  3570. #endif // UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION
  3571. // For testing, we want the ability to push/pop system state even in the player.
  3572. // However, we don't want it in release players.
  3573. #if DEVELOPMENT_BUILD || UNITY_EDITOR
  3574. /// <summary>
  3575. /// Return the input system to its default state.
  3576. /// </summary>
  3577. private static void Reset(bool enableRemoting = false, IInputRuntime runtime = null)
  3578. {
  3579. Profiler.BeginSample("InputSystem.Reset");
  3580. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  3581. // Note that in a test setup we might enter reset with project-wide actions already enabled but the
  3582. // reset itself has pushed the action system state on the state stack. To avoid action state memory
  3583. // problems we disable actions here and also request asset to be marked dirty and reimported.
  3584. DisableActions(triggerSetupChanged: true);
  3585. if (s_Manager != null)
  3586. s_Manager.actions = null;
  3587. #endif // UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  3588. // Some devices keep globals. Get rid of them by pretending the devices
  3589. // are removed.
  3590. if (s_Manager != null)
  3591. {
  3592. foreach (var device in s_Manager.devices)
  3593. device.NotifyRemoved();
  3594. s_Manager.UninstallGlobals();
  3595. }
  3596. // Create temporary settings. In the tests, this is all we need. But outside of tests,d
  3597. // this should get replaced with an actual InputSettings asset.
  3598. var settings = ScriptableObject.CreateInstance<InputSettings>();
  3599. settings.hideFlags = HideFlags.HideAndDontSave;
  3600. #if UNITY_EDITOR
  3601. s_Manager = new InputManager();
  3602. s_Manager.Initialize(
  3603. runtime: runtime ?? NativeInputRuntime.instance,
  3604. settings: settings);
  3605. s_Manager.m_Runtime.onPlayModeChanged = OnPlayModeChange;
  3606. s_Manager.m_Runtime.onProjectChange = OnProjectChange;
  3607. InputEditorUserSettings.s_Settings = new InputEditorUserSettings.SerializedState();
  3608. if (enableRemoting)
  3609. SetUpRemoting();
  3610. #if !UNITY_DISABLE_DEFAULT_INPUT_PLUGIN_INITIALIZATION
  3611. PerformDefaultPluginInitialization();
  3612. #endif
  3613. #else
  3614. InitializeInPlayer(runtime, settings);
  3615. #endif
  3616. Mouse.s_PlatformMouseDevice = null;
  3617. InputEventListener.s_ObserverState = default;
  3618. InputUser.ResetGlobals();
  3619. EnhancedTouchSupport.Reset();
  3620. // This is the point where we initialise project-wide actions for the Editor, Editor Tests and Player Tests.
  3621. // Note this is too early for editor ! actions is not setup yet.
  3622. #if UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS
  3623. EnableActions();
  3624. #endif
  3625. Profiler.EndSample();
  3626. }
  3627. /// <summary>
  3628. /// Destroy the current setup of the input system.
  3629. /// </summary>
  3630. /// <remarks>
  3631. /// NOTE: This also de-allocates data we're keeping in unmanaged memory!
  3632. /// </remarks>
  3633. private static void Destroy()
  3634. {
  3635. // NOTE: Does not destroy InputSystemObject. We want to destroy input system
  3636. // state repeatedly during tests but we want to not create InputSystemObject
  3637. // over and over.
  3638. s_Manager.Destroy();
  3639. if (s_RemoteConnection != null)
  3640. Object.DestroyImmediate(s_RemoteConnection);
  3641. #if UNITY_EDITOR
  3642. EditorInputControlLayoutCache.Clear();
  3643. InputDeviceDebuggerWindow.s_OnToolbarGUIActions.Clear();
  3644. InputEditorUserSettings.s_Settings = new InputEditorUserSettings.SerializedState();
  3645. #endif
  3646. s_Manager = null;
  3647. s_RemoteConnection = null;
  3648. s_Remote = null;
  3649. }
  3650. /// <summary>
  3651. /// Snapshot of the state used by the input system.
  3652. /// </summary>
  3653. /// <remarks>
  3654. /// Can be taken across domain reloads.
  3655. /// </remarks>
  3656. [Serializable]
  3657. internal struct State
  3658. {
  3659. [NonSerialized] public InputManager manager;
  3660. [NonSerialized] public InputRemoting remote;
  3661. [SerializeField] public RemoteInputPlayerConnection remoteConnection;
  3662. [SerializeField] public InputManager.SerializedState managerState;
  3663. [SerializeField] public InputRemoting.SerializedState remotingState;
  3664. #if UNITY_EDITOR
  3665. [SerializeField] public InputEditorUserSettings.SerializedState userSettings;
  3666. [SerializeField] public string systemObject;
  3667. #endif
  3668. ////TODO: make these saved states capable of surviving domain reloads
  3669. [NonSerialized] public ISavedState inputActionState;
  3670. [NonSerialized] public ISavedState touchState;
  3671. [NonSerialized] public ISavedState inputUserState;
  3672. }
  3673. private static Stack<State> s_SavedStateStack;
  3674. internal static State GetSavedState()
  3675. {
  3676. return s_SavedStateStack.Peek();
  3677. }
  3678. /// <summary>
  3679. /// Push the current state of the input system onto a stack and
  3680. /// reset the system to its default state.
  3681. /// </summary>
  3682. /// <remarks>
  3683. /// The save stack is not able to survive domain reloads. It is intended solely
  3684. /// for use in tests.
  3685. /// </remarks>
  3686. internal static void SaveAndReset(bool enableRemoting = false, IInputRuntime runtime = null)
  3687. {
  3688. if (s_SavedStateStack == null)
  3689. s_SavedStateStack = new Stack<State>();
  3690. ////FIXME: does not preserve global state in InputActionState
  3691. ////TODO: preserve InputUser state
  3692. ////TODO: preserve EnhancedTouchSupport state
  3693. s_SavedStateStack.Push(new State
  3694. {
  3695. manager = s_Manager,
  3696. remote = s_Remote,
  3697. remoteConnection = s_RemoteConnection,
  3698. managerState = s_Manager.SaveState(),
  3699. remotingState = s_Remote?.SaveState() ?? new InputRemoting.SerializedState(),
  3700. #if UNITY_EDITOR
  3701. userSettings = InputEditorUserSettings.s_Settings,
  3702. systemObject = JsonUtility.ToJson(s_SystemObject),
  3703. #endif
  3704. inputActionState = InputActionState.SaveAndResetState(),
  3705. touchState = EnhancedTouch.Touch.SaveAndResetState(),
  3706. inputUserState = InputUser.SaveAndResetState()
  3707. });
  3708. Reset(enableRemoting, runtime ?? InputRuntime.s_Instance); // Keep current runtime.
  3709. }
  3710. ////FIXME: this method doesn't restore things like InputDeviceDebuggerWindow.onToolbarGUI
  3711. /// <summary>
  3712. /// Restore the state of the system from the last state pushed with <see cref="SaveAndReset"/>.
  3713. /// </summary>
  3714. internal static void Restore()
  3715. {
  3716. Debug.Assert(s_SavedStateStack != null && s_SavedStateStack.Count > 0);
  3717. // Load back previous state.
  3718. var state = s_SavedStateStack.Pop();
  3719. state.inputUserState.StaticDisposeCurrentState();
  3720. state.touchState.StaticDisposeCurrentState();
  3721. state.inputActionState.StaticDisposeCurrentState();
  3722. // Nuke what we have.
  3723. Destroy();
  3724. state.inputUserState.RestoreSavedState();
  3725. state.touchState.RestoreSavedState();
  3726. state.inputActionState.RestoreSavedState();
  3727. s_Manager = state.manager;
  3728. s_Remote = state.remote;
  3729. s_RemoteConnection = state.remoteConnection;
  3730. InputUpdate.Restore(state.managerState.updateState);
  3731. s_Manager.InstallRuntime(s_Manager.m_Runtime);
  3732. s_Manager.InstallGlobals();
  3733. s_Manager.ApplySettings();
  3734. #if UNITY_EDITOR
  3735. InputEditorUserSettings.s_Settings = state.userSettings;
  3736. JsonUtility.FromJsonOverwrite(state.systemObject, s_SystemObject);
  3737. #endif
  3738. // Get devices that keep global lists (like Gamepad) to re-initialize them
  3739. // by pretending the devices have been added.
  3740. foreach (var device in devices)
  3741. {
  3742. device.NotifyAdded();
  3743. device.MakeCurrent();
  3744. }
  3745. }
  3746. #endif
  3747. }
  3748. }