File size: 4,719 Bytes
18a519f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine.InputSystem.Utilities;

namespace UnityEngine.InputSystem.LowLevel
{
    /// <summary>
    /// 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.
    /// </summary>
    [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;
        }
    }

    /// <summary>
    /// A struct representing an string of characters generated by an IME for text input.
    /// </summary>
    /// <remarks>
    /// This is the internal representation of character strings in the event stream. It is exposed to user content through the
    /// <see cref="ITextInputReceiver.OnIMECompositionChanged"/> method. It can easily be converted to a normal C# string using
    ///  <see cref="ToString"/>, but is exposed as the raw struct to avoid allocating memory by default.
    /// </remarks>
    [StructLayout(LayoutKind.Explicit, Size = sizeof(int) + sizeof(char) * LowLevel.IMECompositionEvent.kIMECharBufferSize)]
    public unsafe struct IMECompositionString : IEnumerable<char>
    {
        internal struct Enumerator : IEnumerator<char>
        {
            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<char> GetEnumerator()
        {
            return new Enumerator(this);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}