2
2
using System . Collections ;
3
3
using System . Collections . Generic ;
4
4
using System . Runtime . InteropServices ;
5
+ using Unity . Collections ;
6
+ using Unity . Collections . LowLevel . Unsafe ;
5
7
using UnityEngine . InputSystem . Utilities ;
6
8
7
9
namespace UnityEngine . InputSystem . LowLevel
8
10
{
9
- /// <summary>
10
- /// A specialized event that contains the current IME Composition string, if IME is enabled and active.
11
- /// This event contains the entire current string to date, and once a new composition is submitted will send a blank string event.
12
- /// </summary>
11
+ [ Obsolete ( "IMECompositionEvent is obsolete, please use IMECompositionEventVariableSize" ) ]
13
12
[ StructLayout ( LayoutKind . Explicit , Size = InputEvent . kBaseEventSize + sizeof ( int ) + ( sizeof ( char ) * kIMECharBufferSize ) ) ]
14
- public struct IMECompositionEvent : IInputEventTypeInfo
13
+ public unsafe struct IMECompositionEvent : IInputEventTypeInfo
15
14
{
16
15
// These needs to match the native ImeCompositionStringInputEventData settings
17
16
internal const int kIMECharBufferSize = 64 ;
@@ -21,19 +20,95 @@ public struct IMECompositionEvent : IInputEventTypeInfo
21
20
public InputEvent baseEvent ;
22
21
23
22
[ FieldOffset ( InputEvent . kBaseEventSize ) ]
24
- public IMECompositionString compositionString ;
23
+ private int length ;
24
+
25
+ [ FieldOffset ( InputEvent . kBaseEventSize + sizeof ( int ) ) ]
26
+ private fixed char buffer [ kIMECharBufferSize ] ;
27
+
28
+ public IMECompositionString compositionString
29
+ {
30
+ get
31
+ {
32
+ fixed( char * ptr = buffer )
33
+ return new IMECompositionString ( ptr , length ) ;
34
+ }
35
+ }
25
36
26
37
public FourCC typeStatic => Type ;
27
38
28
39
public static IMECompositionEvent Create ( int deviceId , string compositionString , double time )
29
40
{
30
41
var inputEvent = new IMECompositionEvent ( ) ;
31
42
inputEvent . baseEvent = new InputEvent ( Type , InputEvent . kBaseEventSize + sizeof ( int ) + ( sizeof ( char ) * kIMECharBufferSize ) , deviceId , time ) ;
32
- inputEvent . compositionString = new IMECompositionString ( compositionString ) ;
43
+ inputEvent . length = compositionString . Length > kIMECharBufferSize ? kIMECharBufferSize : compositionString . Length ;
44
+ fixed( char * dst = compositionString )
45
+ fixed( char * src = compositionString )
46
+ UnsafeUtility . MemCpy ( dst , src , inputEvent . length * sizeof ( char ) ) ;
33
47
return inputEvent ;
34
48
}
35
49
}
36
50
51
+ /// <summary>
52
+ /// A specialized event that contains the current IME Composition string, if IME is enabled and active.
53
+ /// This event contains the entire current string to date, and once a new composition is submitted will send a blank string event.
54
+ /// </summary>
55
+ [ StructLayout ( LayoutKind . Explicit , Size = InputEvent . kBaseEventSize + sizeof ( int ) ) ]
56
+ public struct IMECompositionEventVariableSize : IInputEventTypeInfo
57
+ {
58
+ // Before we had 0x494D4553 which corresponds to ImeCompositionStringInputEventData fixed size event with 64 character payload.
59
+ // 0x494D4543 corresponds to ImeCompositionInputEventData and is a different event which provides variable size array of characters after the event.
60
+ public const int Type = 0x494D4543 ;
61
+
62
+ [ FieldOffset ( 0 ) ]
63
+ public InputEvent baseEvent ;
64
+
65
+ [ FieldOffset ( InputEvent . kBaseEventSize ) ]
66
+ internal int length ;
67
+
68
+ internal static unsafe char * GetCharsPtr ( IMECompositionEventVariableSize * ev )
69
+ {
70
+ return ( char * ) ( ( byte * ) ev + InputEvent . kBaseEventSize + sizeof ( int ) ) ;
71
+ }
72
+
73
+ public FourCC typeStatic => Type ;
74
+
75
+ /// <summary>
76
+ /// Returns composition string for the given event.
77
+ /// </summary>
78
+ /// <param name="ev">Pointer to the event.</param>
79
+ /// <returns></returns>
80
+ public static unsafe IMECompositionString GetIMECompositionString ( IMECompositionEventVariableSize * ev )
81
+ {
82
+ return new IMECompositionString ( GetCharsPtr ( ev ) , ev ->length ) ;
83
+ }
84
+
85
+ /// <summary>
86
+ /// 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.
87
+ /// </summary>
88
+ /// <param name="deviceId">ID of the device (see <see cref="InputDevice.deviceId") to which the composition event should be sent to. Should be an <see cref="ITextInputReceiver"/> device. Will trigger <see cref="ITextInputReceiver.OnIMECompositionChanged"/> call when processed.</param>
89
+ /// <param name="str">The IME characters to be sent. This can be any length, or left blank to represent a resetting of the IME dialog.</param>
90
+ /// <param name="time">The time in seconds, the event was generated at. This uses the same timeline as <see cref="Time.realtimeSinceStartup"/></param>
91
+ public static unsafe void QueueEvent ( int deviceId , string str , double time )
92
+ {
93
+ var sizeInBytes = ( InputEvent . kBaseEventSize + sizeof ( int ) ) + sizeof ( char ) * str . Length ;
94
+ var eventBuffer = new NativeArray < byte > ( sizeInBytes , Allocator . Temp , NativeArrayOptions . UninitializedMemory ) ;
95
+
96
+ var ev = ( IMECompositionEventVariableSize * ) eventBuffer . GetUnsafePtr ( ) ;
97
+
98
+ ev ->baseEvent = new InputEvent ( Type , sizeInBytes , deviceId , time ) ;
99
+ ev ->length = str . Length ;
100
+
101
+ if ( str . Length > 0 )
102
+ fixed( char * p = str )
103
+ UnsafeUtility . MemCpy ( GetCharsPtr ( ev ) , p , str . Length * sizeof ( char ) ) ;
104
+
105
+ InputSystem . QueueEvent ( new InputEventPtr ( ( InputEvent * ) ev ) ) ;
106
+
107
+ eventBuffer . Dispose ( ) ;
108
+ }
109
+ }
110
+
111
+ //// TODO for v2 remove and replace with just string.
37
112
/// <summary>
38
113
/// A struct representing an string of characters generated by an IME for text input.
39
114
/// </summary>
@@ -42,36 +117,31 @@ public static IMECompositionEvent Create(int deviceId, string compositionString,
42
117
/// <see cref="ITextInputReceiver.OnIMECompositionChanged"/> method. It can easily be converted to a normal C# string using
43
118
/// <see cref="ToString"/>, but is exposed as the raw struct to avoid allocating memory by default.
44
119
/// </remarks>
45
- [ StructLayout ( LayoutKind . Explicit , Size = sizeof ( int ) + sizeof ( char ) * LowLevel . IMECompositionEvent . kIMECharBufferSize ) ]
46
120
public unsafe struct IMECompositionString : IEnumerable < char >
47
121
{
48
- internal struct Enumerator : IEnumerator < char >
122
+ private const int kLegacyIMEEventCharBufferSize = 64 ;
123
+
124
+ private readonly string m_ManagedString ;
125
+ private readonly int m_Size ;
126
+ private fixed char m_FixedBuffer [ kLegacyIMEEventCharBufferSize ] ;
127
+
128
+ private struct FixedBufferEnumerator : IEnumerator < char >
49
129
{
50
- IMECompositionString m_CompositionString ;
51
- char m_CurrentCharacter ;
52
- int m_CurrentIndex ;
130
+ private IMECompositionString m_CompositionString ;
131
+ private int m_CurrentIndex ;
53
132
54
- public Enumerator ( IMECompositionString compositionString )
133
+ public FixedBufferEnumerator ( IMECompositionString compositionString )
55
134
{
56
135
m_CompositionString = compositionString ;
57
- m_CurrentCharacter = '\0 ' ;
58
136
m_CurrentIndex = - 1 ;
59
137
}
60
138
61
139
public bool MoveNext ( )
62
140
{
63
- int size = m_CompositionString . Count ;
64
-
65
- m_CurrentIndex ++ ;
66
-
67
- if ( m_CurrentIndex == size )
141
+ if ( m_CurrentIndex + 1 >= m_CompositionString . Count )
68
142
return false ;
69
143
70
- fixed( char * ptr = m_CompositionString . buffer )
71
- {
72
- m_CurrentCharacter = * ( ptr + m_CurrentIndex ) ;
73
- }
74
-
144
+ m_CurrentIndex ++ ;
75
145
return true ;
76
146
}
77
147
@@ -84,58 +154,73 @@ public void Dispose()
84
154
{
85
155
}
86
156
87
- public char Current => m_CurrentCharacter ;
157
+ public char Current => m_CompositionString [ m_CurrentIndex ] ;
88
158
89
159
object IEnumerator . Current => Current ;
90
160
}
91
161
92
- public int Count => size ;
162
+ public int Count => m_Size ;
93
163
94
164
public char this [ int index ]
95
165
{
96
166
get
97
167
{
168
+ if ( m_ManagedString != null )
169
+ return m_ManagedString [ index ] ;
170
+
98
171
if ( index >= Count || index < 0 )
99
172
throw new ArgumentOutOfRangeException ( nameof ( index ) ) ;
100
173
101
- fixed( char * ptr = buffer )
102
- {
103
- return * ( ptr + index ) ;
104
- }
174
+ return m_FixedBuffer [ index ] ;
105
175
}
106
176
}
107
177
108
- [ FieldOffset ( 0 ) ]
109
- int size ;
110
-
111
- [ FieldOffset ( sizeof ( int ) ) ]
112
- fixed char buffer [ IMECompositionEvent . kIMECharBufferSize ] ;
113
-
114
- public IMECompositionString ( string characters )
178
+ public IMECompositionString ( char * characters , int length )
115
179
{
116
- if ( string . IsNullOrEmpty ( characters ) )
180
+ // only allocate string if we can't fit into fixed buffer
181
+ if ( length <= kLegacyIMEEventCharBufferSize )
182
+ {
183
+ m_ManagedString = null ;
184
+ m_Size = length ;
185
+ if ( m_Size > 0 )
186
+ {
187
+ Debug . Assert ( characters != null ) ;
188
+ fixed( char * dst = m_FixedBuffer )
189
+ UnsafeUtility . MemCpy ( dst , characters , m_Size * sizeof ( char ) ) ;
190
+ }
191
+ }
192
+ else
117
193
{
118
- size = 0 ;
119
- return ;
194
+ m_ManagedString = new string ( characters , 0 , length ) ;
195
+ m_Size = length ;
120
196
}
197
+ }
121
198
122
- Debug . Assert ( characters . Length < IMECompositionEvent . kIMECharBufferSize ) ;
123
- size = characters . Length ;
124
- for ( var i = 0 ; i < size ; i ++ )
125
- buffer [ i ] = characters [ i ] ;
199
+ public IMECompositionString ( string characters )
200
+ {
201
+ // string is already allocated on the heap, so reuse it
202
+ m_ManagedString = characters ;
203
+ m_Size = characters . Length ;
126
204
}
127
205
128
206
public override string ToString ( )
129
207
{
130
- fixed( char * ptr = buffer )
131
- {
132
- return new string ( ptr , 0 , size ) ;
133
- }
208
+ if ( m_Size == 0 )
209
+ return string . Empty ;
210
+
211
+ if ( m_ManagedString != null )
212
+ return m_ManagedString ;
213
+
214
+ fixed( char * ptr = m_FixedBuffer )
215
+ return new string ( ptr , 0 , m_Size ) ;
134
216
}
135
217
136
218
public IEnumerator < char > GetEnumerator ( )
137
219
{
138
- return new Enumerator ( this ) ;
220
+ if ( m_ManagedString != null )
221
+ return m_ManagedString . GetEnumerator ( ) ;
222
+
223
+ return new FixedBufferEnumerator ( this ) ;
139
224
}
140
225
141
226
IEnumerator IEnumerable . GetEnumerator ( )
0 commit comments