From 89e18820cc10d8e12234943354fdb0f0fea5104e Mon Sep 17 00:00:00 2001 From: Tom Baird Date: Mon, 6 Apr 2020 17:36:49 -0700 Subject: [PATCH 1/8] IME Events are now completely variable sized. Updated Tests to match --- Assets/Tests/InputSystem/CoreTests_Devices.cs | 3 +- .../InputSystem/Events/IMECompositionEvent.cs | 122 +++++++++++------- .../InputSystem/InputManager.cs | 2 +- 3 files changed, 77 insertions(+), 50 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_Devices.cs b/Assets/Tests/InputSystem/CoreTests_Devices.cs index 2c8434a5fd..8cb448a488 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices.cs @@ -3954,9 +3954,8 @@ public void Devices_CanListenForIMECompositionEvents() Assert.AreEqual(composition.ToString(), imeCompositionCharacters); }; - var inputEvent = IMECompositionEvent.Create(keyboard.deviceId, imeCompositionCharacters, + IMECompositionEvent.QueueEvent(keyboard.deviceId, imeCompositionCharacters, InputRuntime.s_Instance.currentTime); - InputSystem.QueueEvent(ref inputEvent); InputSystem.Update(); Assert.That(callbackWasCalled, Is.True); diff --git a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs index a9c886269f..24d3a08822 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs @@ -10,27 +10,70 @@ namespace UnityEngine.InputSystem.LowLevel /// A specialized event that contains the current IME Composition string, if IME is enabled and active. /// This event contains the entire current string to date, and once a new composition is submitted will send a blank string event. /// - [StructLayout(LayoutKind.Explicit, Size = InputEvent.kBaseEventSize + sizeof(int) + (sizeof(char) * kIMECharBufferSize))] + [StructLayout(LayoutKind.Explicit, Size = InputEvent.kBaseEventSize + sizeof(int) + (sizeof(char)))] public struct IMECompositionEvent : IInputEventTypeInfo { - // These needs to match the native ImeCompositionStringInputEventData settings - internal const int kIMECharBufferSize = 64; - public const int Type = 0x494D4553; + public const int Type = 0x494D4543; [FieldOffset(0)] public InputEvent baseEvent; [FieldOffset(InputEvent.kBaseEventSize)] - public IMECompositionString compositionString; + public int length; + + [FieldOffset(InputEvent.kBaseEventSize + sizeof(int))] + public char bufferStart; public FourCC typeStatic => Type; - public static IMECompositionEvent Create(int deviceId, string compositionString, double time) + /// + /// Gets an IME Composition String. !-- This Composition String can only be used within the + /// + /// A structure containing the current IME Composition + public IMECompositionString GetComposition() + { + unsafe + { + fixed(char* buffer = &bufferStart) + { + return new IMECompositionString(new IntPtr(buffer), length); + } + } + } + + /// + /// Queues up an IME Composition Event.!-- This event is for unit testing and debug purposes, and is slow, due to the variable way that IME Composition events work. + /// + /// The Id you'd like to send the composition event at. + /// The IME composition string. + /// The time that the event occurred at. + public static void QueueEvent(int deviceId, string str, double time) { - var inputEvent = new IMECompositionEvent(); - inputEvent.baseEvent = new InputEvent(Type, InputEvent.kBaseEventSize + sizeof(int) + (sizeof(char) * kIMECharBufferSize), deviceId, time); - inputEvent.compositionString = new IMECompositionString(compositionString); - return inputEvent; + unsafe + { + int sizeInBytes = (InputEvent.kBaseEventSize + sizeof(int) + sizeof(char)) + (sizeof(char) * str.Length); + IntPtr evtPtr = Marshal.AllocHGlobal(sizeInBytes); + byte* ptr = (byte*)evtPtr.ToPointer(); + InputEvent* evt = (InputEvent*)ptr; + + *evt = new InputEvent(Type, sizeInBytes, deviceId, time); + ptr += InputEvent.kBaseEventSize; + + int* lengthPtr = (int*)ptr; + *lengthPtr = str.Length; + + ptr += sizeof(int); + + fixed (char* p = str) + { + Buffer.MemoryCopy(p, ptr, str.Length * sizeof(char), str.Length * sizeof(char)); + } + + InputSystem.QueueEvent(new InputEventPtr(evt)); + Marshal.FreeHGlobal(evtPtr); + } + + } } @@ -42,35 +85,32 @@ public static IMECompositionEvent Create(int deviceId, string compositionString, /// method. It can easily be converted to a normal C# string using /// , but is exposed as the raw struct to avoid allocating memory by default. /// - [StructLayout(LayoutKind.Explicit, Size = sizeof(int) + sizeof(char) * LowLevel.IMECompositionEvent.kIMECharBufferSize)] public unsafe struct IMECompositionString : IEnumerable { internal struct Enumerator : IEnumerator { - IMECompositionString m_CompositionString; + IntPtr m_BufferStart; + int m_CharacterCount; char m_CurrentCharacter; int m_CurrentIndex; public Enumerator(IMECompositionString compositionString) { - m_CompositionString = compositionString; + m_BufferStart = compositionString.m_CharBuffer; + m_CharacterCount = compositionString.length; m_CurrentCharacter = '\0'; m_CurrentIndex = -1; } public bool MoveNext() { - int size = m_CompositionString.Count; - m_CurrentIndex++; - if (m_CurrentIndex == size) + if (m_CurrentIndex == m_CharacterCount) return false; - fixed(char* ptr = m_CompositionString.buffer) - { - m_CurrentCharacter = *(ptr + m_CurrentIndex); - } + char* ptr = (char*)m_BufferStart.ToPointer(); + m_CurrentCharacter = *(ptr + m_CurrentIndex); return true; } @@ -89,48 +129,36 @@ public void Dispose() object IEnumerator.Current => Current; } - public int Count => size; + int m_Length; + public int length { get { return m_Length; }} + + IntPtr m_CharBuffer; public char this[int index] { get { - if (index >= Count || index < 0) + if (index >= m_Length || index < 0) throw new ArgumentOutOfRangeException(nameof(index)); - fixed(char* ptr = buffer) - { - return *(ptr + index); - } + char* ptr = (char*)m_CharBuffer.ToPointer(); + return *(ptr + index); } } - [FieldOffset(0)] - int size; - [FieldOffset(sizeof(int))] - fixed char buffer[IMECompositionEvent.kIMECharBufferSize]; - - public IMECompositionString(string characters) + public IMECompositionString(IntPtr charBuffer, int length) { - if (string.IsNullOrEmpty(characters)) - { - size = 0; - return; - } - - Debug.Assert(characters.Length < IMECompositionEvent.kIMECharBufferSize); - size = characters.Length; - for (var i = 0; i < size; i++) - buffer[i] = characters[i]; + m_Length = length; + m_CharBuffer = charBuffer; } + //static StringBuilder sb; + public override string ToString() { - fixed(char* ptr = buffer) - { - return new string(ptr, 0, size); - } + char* ptr = (char*)m_CharBuffer.ToPointer(); + return new string(ptr, 0, m_Length); } public IEnumerator GetEnumerator() @@ -143,4 +171,4 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } } -} +} \ No newline at end of file diff --git a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs index c6e324c692..c2fa165693 100644 --- a/Packages/com.unity.inputsystem/InputSystem/InputManager.cs +++ b/Packages/com.unity.inputsystem/InputSystem/InputManager.cs @@ -2748,7 +2748,7 @@ private unsafe void OnUpdate(InputUpdateType updateType, ref InputEventBuffer ev { var imeEventPtr = (IMECompositionEvent*)currentEventReadPtr; var textInputReceiver = device as ITextInputReceiver; - textInputReceiver?.OnIMECompositionChanged(imeEventPtr->compositionString); + textInputReceiver?.OnIMECompositionChanged(imeEventPtr->GetComposition()); break; } From db7cf4bbab55ad98e8c9fbf8320dea5271c096a6 Mon Sep 17 00:00:00 2001 From: Tom Baird Date: Tue, 7 Apr 2020 19:05:11 -0700 Subject: [PATCH 2/8] Ran Formatter --- .../InputSystem/Events/IMECompositionEvent.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs index 24d3a08822..dc8d4c3304 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs @@ -27,7 +27,7 @@ public struct IMECompositionEvent : IInputEventTypeInfo public FourCC typeStatic => Type; /// - /// Gets an IME Composition String. !-- This Composition String can only be used within the + /// Gets an IME Composition String. !-- This Composition String can only be used within the /// /// A structure containing the current IME Composition public IMECompositionString GetComposition() @@ -64,16 +64,14 @@ public static void QueueEvent(int deviceId, string str, double time) ptr += sizeof(int); - fixed (char* p = str) + fixed(char* p = str) { Buffer.MemoryCopy(p, ptr, str.Length * sizeof(char), str.Length * sizeof(char)); } - + InputSystem.QueueEvent(new InputEventPtr(evt)); Marshal.FreeHGlobal(evtPtr); } - - } } @@ -129,7 +127,7 @@ public void Dispose() object IEnumerator.Current => Current; } - int m_Length; + int m_Length; public int length { get { return m_Length; }} IntPtr m_CharBuffer; @@ -171,4 +169,4 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } } -} \ No newline at end of file +} From ca0bc82fa76da3aa9aef446337b2c24c82cb5ef6 Mon Sep 17 00:00:00 2001 From: Tom Baird Date: Wed, 15 Apr 2020 10:33:30 -0700 Subject: [PATCH 3/8] Comments and internalize IntPtr functions --- .../InputSystem/Events/IMECompositionEvent.cs | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs index dc8d4c3304..972b8cea3a 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs @@ -16,21 +16,17 @@ public struct IMECompositionEvent : IInputEventTypeInfo public const int Type = 0x494D4543; [FieldOffset(0)] - public InputEvent baseEvent; + internal InputEvent baseEvent; [FieldOffset(InputEvent.kBaseEventSize)] - public int length; + internal int length; [FieldOffset(InputEvent.kBaseEventSize + sizeof(int))] - public char bufferStart; + internal char bufferStart; public FourCC typeStatic => Type; - /// - /// Gets an IME Composition String. !-- This Composition String can only be used within the - /// - /// A structure containing the current IME Composition - public IMECompositionString GetComposition() + internal IMECompositionString GetComposition() { unsafe { @@ -128,10 +124,19 @@ public void Dispose() } int m_Length; + /// + /// The number of characters in the current IME Composition + /// + /// The Length of the Composition public int length { get { return m_Length; }} IntPtr m_CharBuffer; + + /// + /// An Indexer into an individual character in the IME Composition. Will throw an out of range exception if the index is greater than the length of the composition. + /// + /// The character at the requested index public char this[int index] { get @@ -145,20 +150,26 @@ public char this[int index] } - public IMECompositionString(IntPtr charBuffer, int length) + internal IMECompositionString(IntPtr charBuffer, int length) { m_Length = length; m_CharBuffer = charBuffer; } - //static StringBuilder sb; - + /// + /// Returns the IME Composition as a new string + /// + /// The IME Composition as a string public override string ToString() { char* ptr = (char*)m_CharBuffer.ToPointer(); return new string(ptr, 0, m_Length); } + /// + /// Gets an Enumerator that enumerates over the individual IME Composition characters + /// + /// The IME Composition Enuemrator public IEnumerator GetEnumerator() { return new Enumerator(this); From 161da0afa466ff9908422025d9e17d5643bfd89a Mon Sep 17 00:00:00 2001 From: Tom Baird Date: Sun, 10 May 2020 22:00:30 -0700 Subject: [PATCH 4/8] PR Feedback and additional test. --- Assets/Tests/InputSystem/CoreTests_Devices.cs | 23 ++++++++ .../InputSystem/Events/IMECompositionEvent.cs | 58 ++++++++++--------- 2 files changed, 53 insertions(+), 28 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_Devices.cs b/Assets/Tests/InputSystem/CoreTests_Devices.cs index 8cb448a488..30c0ded6e0 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices.cs @@ -3961,6 +3961,29 @@ public void Devices_CanListenForIMECompositionEvents() Assert.That(callbackWasCalled, Is.True); } + [Test] + [Category("Devices")] + public void Devices_CanReadEmptyIMECompositionEvents() + { + const string imeCompositionCharacters = ""; + var callbackWasCalled = false; + + var keyboard = InputSystem.AddDevice(); + keyboard.onIMECompositionChange += composition => + { + Assert.That(callbackWasCalled, Is.False); + callbackWasCalled = true; + Assert.AreEqual(composition.length, 0); + Assert.AreEqual(composition.ToString(), imeCompositionCharacters); + }; + + IMECompositionEvent.QueueEvent(keyboard.deviceId, imeCompositionCharacters, + InputRuntime.s_Instance.currentTime); + InputSystem.Update(); + + Assert.That(callbackWasCalled, Is.True); + } + [Test] [Category("Devices")] public unsafe void Devices_CanEnableAndDisableIME() diff --git a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs index 972b8cea3a..768e097733 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs @@ -2,6 +2,8 @@ using System.Collections; using System.Collections.Generic; using System.Runtime.InteropServices; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine.InputSystem.Utilities; namespace UnityEngine.InputSystem.LowLevel @@ -38,35 +40,31 @@ internal IMECompositionString GetComposition() } /// - /// Queues up an IME Composition Event.!-- This event is for unit testing and debug purposes, and is slow, due to the variable way that IME Composition events work. + /// Queues up an IME Composition Event. IME Event sizes are variable, and this simplifies the process of aligning up the Input Event information and actual IME composition string. /// - /// The Id you'd like to send the composition event at. - /// The IME composition string. - /// The time that the event occurred at. + /// ID of the device (see device. Will trigger call when processed. + /// The IME characters to be sent. This can be any length, or left blank to represent a resetting of the IME dialog. + /// The time in seconds, the event was generated at. This uses the same timeline as public static void QueueEvent(int deviceId, string str, double time) { unsafe { int sizeInBytes = (InputEvent.kBaseEventSize + sizeof(int) + sizeof(char)) + (sizeof(char) * str.Length); - IntPtr evtPtr = Marshal.AllocHGlobal(sizeInBytes); - byte* ptr = (byte*)evtPtr.ToPointer(); - InputEvent* evt = (InputEvent*)ptr; + NativeArray eventBuffer = new NativeArray(sizeInBytes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); + eventBuffer.ReinterpretStore(0, new InputEvent(Type, sizeInBytes, deviceId, time)); + eventBuffer.ReinterpretStore(InputEvent.kBaseEventSize, str.Length); - *evt = new InputEvent(Type, sizeInBytes, deviceId, time); - ptr += InputEvent.kBaseEventSize; + //Copy string as array of characters. + NativeArray imeCharacters = new NativeArray(str.ToCharArray(), Allocator.Temp); + NativeArray.Copy(imeCharacters.Reinterpret(2), 0, eventBuffer, InputEvent.kBaseEventSize + sizeof(int), sizeof(char) * str.Length); + //Add Null terminator to string + eventBuffer.ReinterpretStore((InputEvent.kBaseEventSize + sizeof(int) + (sizeof(char) * str.Length)), '\0'); - int* lengthPtr = (int*)ptr; - *lengthPtr = str.Length; + byte[] eventBytes = eventBuffer.ToArray(); + void* ptr = UnsafeUtility.PinGCArrayAndGetDataAddress(eventBytes, out ulong eventHandle); + InputSystem.QueueEvent(new InputEventPtr((InputEvent*)ptr)); + UnsafeUtility.ReleaseGCObject(eventHandle); - ptr += sizeof(int); - - fixed(char* p = str) - { - Buffer.MemoryCopy(p, ptr, str.Length * sizeof(char), str.Length * sizeof(char)); - } - - InputSystem.QueueEvent(new InputEventPtr(evt)); - Marshal.FreeHGlobal(evtPtr); } } } @@ -78,6 +76,8 @@ public static void QueueEvent(int deviceId, string str, double time) /// This is the internal representation of character strings in the event stream. It is exposed to user content through the /// method. It can easily be converted to a normal C# string using /// , but is exposed as the raw struct to avoid allocating memory by default. + /// + /// Because this string does not allocate, it is only valid, and can only be read within the that it is recieved, and will otherwise return an invalid composition. /// public unsafe struct IMECompositionString : IEnumerable { @@ -125,9 +125,11 @@ public void Dispose() int m_Length; /// - /// The number of characters in the current IME Composition + /// This returns the total number of characters in the IME composition. /// - /// The Length of the Composition + /// + /// If 0, this event represents a clearing of the current IME dialog. + /// public int length { get { return m_Length; }} IntPtr m_CharBuffer; @@ -136,7 +138,8 @@ public void Dispose() /// /// An Indexer into an individual character in the IME Composition. Will throw an out of range exception if the index is greater than the length of the composition. /// - /// The character at the requested index + /// The requested index is greater than the of the composition. + /// Returns the character at the requested index in UTF-32 encoding. public char this[int index] { get @@ -157,8 +160,11 @@ internal IMECompositionString(IntPtr charBuffer, int length) } /// - /// Returns the IME Composition as a new string + /// Converts the IMEComposition to a new string. /// + /// + /// This will generate garbage by allocating a new string. + /// /// The IME Composition as a string public override string ToString() { @@ -166,10 +172,6 @@ public override string ToString() return new string(ptr, 0, m_Length); } - /// - /// Gets an Enumerator that enumerates over the individual IME Composition characters - /// - /// The IME Composition Enuemrator public IEnumerator GetEnumerator() { return new Enumerator(this); From cf1af0ac38b69716bf3b3cdf7d15bcb947cf6f3d Mon Sep 17 00:00:00 2001 From: Tom Baird Date: Mon, 11 May 2020 09:55:04 -0700 Subject: [PATCH 5/8] Second attempt using NativeArray --- .../InputSystem/Events/IMECompositionEvent.cs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs index 768e097733..722547da4f 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs @@ -51,20 +51,24 @@ public static void QueueEvent(int deviceId, string str, double time) { int sizeInBytes = (InputEvent.kBaseEventSize + sizeof(int) + sizeof(char)) + (sizeof(char) * str.Length); NativeArray eventBuffer = new NativeArray(sizeInBytes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); - eventBuffer.ReinterpretStore(0, new InputEvent(Type, sizeInBytes, deviceId, time)); - eventBuffer.ReinterpretStore(InputEvent.kBaseEventSize, str.Length); + + byte* ptr = (byte*)NativeArrayUnsafeUtility.GetUnsafePtr(eventBuffer); + InputEvent* evt = (InputEvent*)ptr; - //Copy string as array of characters. - NativeArray imeCharacters = new NativeArray(str.ToCharArray(), Allocator.Temp); - NativeArray.Copy(imeCharacters.Reinterpret(2), 0, eventBuffer, InputEvent.kBaseEventSize + sizeof(int), sizeof(char) * str.Length); - //Add Null terminator to string - eventBuffer.ReinterpretStore((InputEvent.kBaseEventSize + sizeof(int) + (sizeof(char) * str.Length)), '\0'); + *evt = new InputEvent(Type, sizeInBytes, deviceId, time); + ptr += InputEvent.kBaseEventSize; - byte[] eventBytes = eventBuffer.ToArray(); - void* ptr = UnsafeUtility.PinGCArrayAndGetDataAddress(eventBytes, out ulong eventHandle); - InputSystem.QueueEvent(new InputEventPtr((InputEvent*)ptr)); - UnsafeUtility.ReleaseGCObject(eventHandle); + int* lengthPtr = (int*)ptr; + *lengthPtr = str.Length; + ptr += sizeof(int); + + fixed(char* p = str) + { + UnsafeUtility.MemCpy(ptr, p, str.Length * sizeof(char)); + } + + InputSystem.QueueEvent(new InputEventPtr(evt)); } } } From a8ea785557b2417f46e08ef307c818adcdc06136 Mon Sep 17 00:00:00 2001 From: StayTalm Date: Mon, 10 Aug 2020 18:49:01 -0700 Subject: [PATCH 6/8] Run Formatter --- .../InputSystem/Events/IMECompositionEvent.cs | 6 +++--- .../InputSystem/Plugins/XR/Controls/PoseControl.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs index 722547da4f..90ce1f54fd 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs @@ -51,7 +51,7 @@ public static void QueueEvent(int deviceId, string str, double time) { int sizeInBytes = (InputEvent.kBaseEventSize + sizeof(int) + sizeof(char)) + (sizeof(char) * str.Length); NativeArray eventBuffer = new NativeArray(sizeInBytes, Allocator.Temp, NativeArrayOptions.UninitializedMemory); - + byte* ptr = (byte*)NativeArrayUnsafeUtility.GetUnsafePtr(eventBuffer); InputEvent* evt = (InputEvent*)ptr; @@ -62,7 +62,7 @@ public static void QueueEvent(int deviceId, string str, double time) *lengthPtr = str.Length; ptr += sizeof(int); - + fixed(char* p = str) { UnsafeUtility.MemCpy(ptr, p, str.Length * sizeof(char)); @@ -80,7 +80,7 @@ public static void QueueEvent(int deviceId, string str, double time) /// This is the internal representation of character strings in the event stream. It is exposed to user content through the /// method. It can easily be converted to a normal C# string using /// , but is exposed as the raw struct to avoid allocating memory by default. - /// + /// /// Because this string does not allocate, it is only valid, and can only be read within the that it is recieved, and will otherwise return an invalid composition. /// public unsafe struct IMECompositionString : IEnumerable diff --git a/Packages/com.unity.inputsystem/InputSystem/Plugins/XR/Controls/PoseControl.cs b/Packages/com.unity.inputsystem/InputSystem/Plugins/XR/Controls/PoseControl.cs index 40587e75fe..f8b48b894d 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Plugins/XR/Controls/PoseControl.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Plugins/XR/Controls/PoseControl.cs @@ -33,7 +33,7 @@ public struct PoseState : IInputStateTypeInfo /// /// Constructor for PoseStates. - /// + /// /// Useful for creating PoseStates locally (not from ). /// /// Value to use for @@ -64,7 +64,7 @@ public PoseState(bool isTracked, TrackingState trackingState, Vector3 position, /// /// A Flags Enumeration specifying which other fields in the pose state are valid. /// - [FieldOffset(4), InputControl( displayName = "Tracking State", layout = "Integer")] + [FieldOffset(4), InputControl(displayName = "Tracking State", layout = "Integer")] public TrackingState trackingState; /// @@ -119,7 +119,7 @@ public PoseState(bool isTracked, TrackingState trackingState, Vector3 position, /// will not work correctly with a different memory layouts. Additional fields may /// be appended to the struct but what's there in the struct has to be located /// at exactly those memory addresses. - /// + /// /// For more information on tracking origins see . /// [Preserve, InputControlLayout(stateType = typeof(PoseState))] From d8dfbc4dbd753fe2e1c5d44785ab3f28cab7e50f Mon Sep 17 00:00:00 2001 From: StayTalm Date: Tue, 11 Aug 2020 11:31:19 -0700 Subject: [PATCH 7/8] Swap back to keeping the exisiting event format and creating a second method for getting hte NativeArray to avoid breaking API. --- Assets/Tests/InputSystem/CoreTests_Devices.cs | 2 +- .../InputSystem/Events/IMECompositionEvent.cs | 131 ++++++++++++------ 2 files changed, 86 insertions(+), 47 deletions(-) diff --git a/Assets/Tests/InputSystem/CoreTests_Devices.cs b/Assets/Tests/InputSystem/CoreTests_Devices.cs index 3c6e5a2205..a206997bab 100644 --- a/Assets/Tests/InputSystem/CoreTests_Devices.cs +++ b/Assets/Tests/InputSystem/CoreTests_Devices.cs @@ -3978,7 +3978,7 @@ public void Devices_CanReadEmptyIMECompositionEvents() { Assert.That(callbackWasCalled, Is.False); callbackWasCalled = true; - Assert.AreEqual(composition.length, 0); + Assert.AreEqual(composition.Count, 0); Assert.AreEqual(composition.ToString(), imeCompositionCharacters); }; diff --git a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs index 90ce1f54fd..74dc22f461 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs @@ -8,6 +8,22 @@ namespace UnityEngine.InputSystem.LowLevel { + /// + /// Helper methods to convert NativeArray objects to C# strings. + /// + static class NativeArrayStringExtension + { + /// + /// Extension method to convert a NativeArray/ to a C# string. + /// + /// The NativeArray containing the string data. + /// A string representation of the Native Array character buffer. + public static unsafe string ToString(this NativeArray c) + { + return new string((char*)NativeArrayUnsafeUtility.GetUnsafePtr(c), 0, c.Length); + } + } + /// /// A specialized event that contains the current IME Composition string, if IME is enabled and active. /// This event contains the entire current string to date, and once a new composition is submitted will send a blank string event. @@ -15,10 +31,11 @@ namespace UnityEngine.InputSystem.LowLevel [StructLayout(LayoutKind.Explicit, Size = InputEvent.kBaseEventSize + sizeof(int) + (sizeof(char)))] public struct IMECompositionEvent : IInputEventTypeInfo { + internal const int kIMECharBufferSize = 64; public const int Type = 0x494D4543; [FieldOffset(0)] - internal InputEvent baseEvent; + public InputEvent baseEvent; [FieldOffset(InputEvent.kBaseEventSize)] internal int length; @@ -30,13 +47,28 @@ public struct IMECompositionEvent : IInputEventTypeInfo internal IMECompositionString GetComposition() { - unsafe + var nativeArray = GetCharacters(); + var str = new IMECompositionString(nativeArray); + nativeArray.Dispose(); + return str; + } + + /// + /// Gets the IME characters packed into a variable sized NativeArray. + /// + /// + /// It is the callers responsibility to dispose of the NativeArray. + /// + /// The IME Characters for this event. + public unsafe NativeArray GetCharacters() + { + var characters = new NativeArray(length + sizeof(char), Allocator.Temp); + var ptr = NativeArrayUnsafeUtility.GetUnsafePtr(characters); + fixed(char* buffer = &bufferStart) { - fixed(char* buffer = &bufferStart) - { - return new IMECompositionString(new IntPtr(buffer), length); - } + UnsafeUtility.MemCpy(ptr, buffer, length + sizeof(char)); } + return characters; } /// @@ -80,35 +112,36 @@ public static void QueueEvent(int deviceId, string str, double time) /// This is the internal representation of character strings in the event stream. It is exposed to user content through the /// method. It can easily be converted to a normal C# string using /// , but is exposed as the raw struct to avoid allocating memory by default. - /// - /// Because this string does not allocate, it is only valid, and can only be read within the that it is recieved, and will otherwise return an invalid composition. /// + [StructLayout(LayoutKind.Explicit, Size = sizeof(int) + sizeof(char) * LowLevel.IMECompositionEvent.kIMECharBufferSize)] public unsafe struct IMECompositionString : IEnumerable { internal struct Enumerator : IEnumerator { - IntPtr m_BufferStart; - int m_CharacterCount; + IMECompositionString m_CompositionString; char m_CurrentCharacter; int m_CurrentIndex; public Enumerator(IMECompositionString compositionString) { - m_BufferStart = compositionString.m_CharBuffer; - m_CharacterCount = compositionString.length; + m_CompositionString = compositionString; m_CurrentCharacter = '\0'; m_CurrentIndex = -1; } public bool MoveNext() { + int size = m_CompositionString.Count; + m_CurrentIndex++; - if (m_CurrentIndex == m_CharacterCount) + if (m_CurrentIndex == size) return false; - char* ptr = (char*)m_BufferStart.ToPointer(); - m_CurrentCharacter = *(ptr + m_CurrentIndex); + fixed (char* ptr = m_CompositionString.buffer) + { + m_CurrentCharacter = *(ptr + m_CurrentIndex); + } return true; } @@ -127,53 +160,59 @@ public void Dispose() object IEnumerator.Current => Current; } - int m_Length; - /// - /// This returns the total number of characters in the IME composition. - /// - /// - /// If 0, this event represents a clearing of the current IME dialog. - /// - public int length { get { return m_Length; }} - - IntPtr m_CharBuffer; + public int Count => size; - - /// - /// An Indexer into an individual character in the IME Composition. Will throw an out of range exception if the index is greater than the length of the composition. - /// - /// The requested index is greater than the of the composition. - /// Returns the character at the requested index in UTF-32 encoding. public char this[int index] { get { - if (index >= m_Length || index < 0) + if (index >= Count || index < 0) throw new ArgumentOutOfRangeException(nameof(index)); - char* ptr = (char*)m_CharBuffer.ToPointer(); - return *(ptr + index); + fixed (char* ptr = buffer) + { + return *(ptr + index); + } } } + [FieldOffset(0)] + int size; + + [FieldOffset(sizeof(int))] + fixed char buffer[IMECompositionEvent.kIMECharBufferSize]; + + public IMECompositionString(NativeArray characters) + { + int copySize = IMECompositionEvent.kIMECharBufferSize < characters.Length ? IMECompositionEvent.kIMECharBufferSize : characters.Length; + fixed (char* ptr = buffer) + { + void* arrayPtr = NativeArrayUnsafeUtility.GetUnsafePtr(characters); + UnsafeUtility.MemCpy(ptr, arrayPtr, copySize * sizeof(char)); + } + size = copySize; + } - internal IMECompositionString(IntPtr charBuffer, int length) + public IMECompositionString(string characters) { - m_Length = length; - m_CharBuffer = charBuffer; + if (string.IsNullOrEmpty(characters)) + { + size = 0; + return; + } + + Debug.Assert(characters.Length < IMECompositionEvent.kIMECharBufferSize); + size = characters.Length; + for (var i = 0; i < size; i++) + buffer[i] = characters[i]; } - /// - /// Converts the IMEComposition to a new string. - /// - /// - /// This will generate garbage by allocating a new string. - /// - /// The IME Composition as a string public override string ToString() { - char* ptr = (char*)m_CharBuffer.ToPointer(); - return new string(ptr, 0, m_Length); + fixed (char* ptr = buffer) + { + return new string(ptr, 0, size); + } } public IEnumerator GetEnumerator() From cad29a87fc8615e0146f4d0808e3b9802a0e27ca Mon Sep 17 00:00:00 2001 From: StayTalm Date: Tue, 11 Aug 2020 11:35:00 -0700 Subject: [PATCH 8/8] Formatting --- .../InputSystem/Events/IMECompositionEvent.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs index 74dc22f461..399d4d5ff7 100644 --- a/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs +++ b/Packages/com.unity.inputsystem/InputSystem/Events/IMECompositionEvent.cs @@ -138,7 +138,7 @@ public bool MoveNext() if (m_CurrentIndex == size) return false; - fixed (char* ptr = m_CompositionString.buffer) + fixed(char* ptr = m_CompositionString.buffer) { m_CurrentCharacter = *(ptr + m_CurrentIndex); } @@ -169,7 +169,7 @@ public char this[int index] if (index >= Count || index < 0) throw new ArgumentOutOfRangeException(nameof(index)); - fixed (char* ptr = buffer) + fixed(char* ptr = buffer) { return *(ptr + index); } @@ -185,7 +185,7 @@ public char this[int index] public IMECompositionString(NativeArray characters) { int copySize = IMECompositionEvent.kIMECharBufferSize < characters.Length ? IMECompositionEvent.kIMECharBufferSize : characters.Length; - fixed (char* ptr = buffer) + fixed(char* ptr = buffer) { void* arrayPtr = NativeArrayUnsafeUtility.GetUnsafePtr(characters); UnsafeUtility.MemCpy(ptr, arrayPtr, copySize * sizeof(char)); @@ -209,7 +209,7 @@ public IMECompositionString(string characters) public override string ToString() { - fixed (char* ptr = buffer) + fixed(char* ptr = buffer) { return new string(ptr, 0, size); }