using System; using System.Collections; using System.Collections.Generic; using System.Runtime.InteropServices; using UnityEngine.InputSystem.Utilities; 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))] public struct IMECompositionEvent : IInputEventTypeInfo { // These needs to match the native ImeCompositionStringInputEventData settings internal const int kIMECharBufferSize = 64; public const int Type = 0x494D4553; [FieldOffset(0)] public InputEvent baseEvent; [FieldOffset(InputEvent.kBaseEventSize)] public IMECompositionString compositionString; public FourCC typeStatic => Type; public static IMECompositionEvent Create(int deviceId, string compositionString, 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; } } /// /// A struct representing an string of characters generated by an IME for text input. /// /// /// 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. /// [StructLayout(LayoutKind.Explicit, Size = sizeof(int) + sizeof(char) * LowLevel.IMECompositionEvent.kIMECharBufferSize)] public unsafe struct IMECompositionString : IEnumerable { internal struct Enumerator : IEnumerator { IMECompositionString m_CompositionString; char m_CurrentCharacter; int m_CurrentIndex; public Enumerator(IMECompositionString compositionString) { m_CompositionString = compositionString; m_CurrentCharacter = '\0'; m_CurrentIndex = -1; } public bool MoveNext() { int size = m_CompositionString.Count; m_CurrentIndex++; if (m_CurrentIndex == size) return false; fixed(char* ptr = m_CompositionString.buffer) { m_CurrentCharacter = *(ptr + m_CurrentIndex); } return true; } public void Reset() { m_CurrentIndex = -1; } public void Dispose() { } public char Current => m_CurrentCharacter; object IEnumerator.Current => Current; } public int Count => size; public char this[int index] { get { if (index >= Count || index < 0) throw new ArgumentOutOfRangeException(nameof(index)); fixed(char* ptr = buffer) { return *(ptr + index); } } } [FieldOffset(0)] int size; [FieldOffset(sizeof(int))] fixed char buffer[IMECompositionEvent.kIMECharBufferSize]; public IMECompositionString(string characters) { 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]; } public override string ToString() { fixed(char* ptr = buffer) { return new string(ptr, 0, size); } } public IEnumerator GetEnumerator() { return new Enumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } }