|
5 | 5 |
|
6 | 6 | using Vanara.PInvoke; |
7 | 7 |
|
8 | | -namespace Nivot.Terminal |
| 8 | +namespace Samples.Terminal |
9 | 9 | { |
10 | 10 | /// <summary> |
11 | 11 | /// Provides a Stream-oriented view over the console's input buffer key events |
@@ -93,61 +93,65 @@ public override int Read(byte[] buffer, int offset, int count) |
93 | 93 | var records = new Kernel32.INPUT_RECORD[BufferSize]; |
94 | 94 |
|
95 | 95 | // begin input loop |
96 | | - waitForInput: |
97 | | - |
98 | | - var readSuccess = Kernel32.ReadConsoleInput(_handle, records, 256, out var recordsRead); |
99 | | - Debug.WriteLine("Read {0} input record(s)", recordsRead); |
100 | | - |
101 | | - if (readSuccess && recordsRead > 0) |
| 96 | + do |
102 | 97 | { |
103 | | - for (var index = 0; index < recordsRead; index++) |
| 98 | + var readSuccess = Kernel32.ReadConsoleInput(_handle, records, BufferSize, out var recordsRead); |
| 99 | + Debug.WriteLine("Read {0} input record(s)", recordsRead); |
| 100 | + |
| 101 | + // some of the arithmetic here is deliberately more explicit than it needs to be |
| 102 | + // in order to show how 16-bit unicode WCHARs are packed into the buffer. The console |
| 103 | + // subsystem is one of the last bastions of UCS-2, so until UTF-16 is fully adopted |
| 104 | + // the two-byte character assumptions below will hold. |
| 105 | + if (readSuccess && recordsRead > 0) |
104 | 106 | { |
105 | | - var record = records[index]; |
106 | | - |
107 | | - if (record.EventType == Kernel32.EVENT_TYPE.KEY_EVENT) |
| 107 | + for (var index = 0; index < recordsRead; index++) |
108 | 108 | { |
109 | | - // skip key up events - if not, every key will be duped in the stream |
110 | | - if (record.Event.KeyEvent.bKeyDown == false) continue; |
| 109 | + var record = records[index]; |
111 | 110 |
|
112 | | - // pack ucs-2/utf-16le/unicode chars into position in our byte[] buffer. |
113 | | - var glyph = (ushort) record.Event.KeyEvent.uChar; |
| 111 | + if (record.EventType == Kernel32.EVENT_TYPE.KEY_EVENT) |
| 112 | + { |
| 113 | + // skip key up events - if not, every key will be duped in the stream |
| 114 | + if (record.Event.KeyEvent.bKeyDown == false) continue; |
114 | 115 |
|
115 | | - var lsb = (byte) (glyph & 0xFFu); |
116 | | - var msb = (byte) ((glyph >> 8) & 0xFFu); |
| 116 | + // pack ucs-2/utf-16le/unicode chars into position in our byte[] buffer. |
| 117 | + var glyph = (ushort) record.Event.KeyEvent.uChar; |
117 | 118 |
|
118 | | - // ensure we accommodate key repeat counts |
119 | | - for (var n = 0; n < record.Event.KeyEvent.wRepeatCount; n++) |
120 | | - { |
121 | | - buffer[offset + charsRead * BytesPerWChar] = lsb; |
122 | | - buffer[offset + charsRead * BytesPerWChar + 1] = msb; |
| 119 | + var lsb = (byte) (glyph & 0xFFu); |
| 120 | + var msb = (byte) ((glyph >> 8) & 0xFFu); |
| 121 | + |
| 122 | + // ensure we accommodate key repeat counts |
| 123 | + for (var n = 0; n < record.Event.KeyEvent.wRepeatCount; n++) |
| 124 | + { |
| 125 | + buffer[offset + charsRead * BytesPerWChar] = lsb; |
| 126 | + buffer[offset + charsRead * BytesPerWChar + 1] = msb; |
123 | 127 |
|
124 | | - charsRead++; |
| 128 | + charsRead++; |
| 129 | + } |
125 | 130 | } |
126 | | - } |
127 | | - else |
128 | | - { |
129 | | - // ignore focus events (not doing so makes debugging absolutely hilarious) |
130 | | - if (record.EventType != Kernel32.EVENT_TYPE.FOCUS_EVENT) |
| 131 | + else |
131 | 132 | { |
132 | | - // I assume success adding records - this is not so critical |
133 | | - // if it is critical to you, loop on this with a miniscule delay |
134 | | - _nonKeyEvents.TryAdd(record); |
| 133 | + // ignore focus events; not doing so makes debugging absolutely hilarious |
| 134 | + // when breakpoints repeatedly cause focus events to occur as your view toggles |
| 135 | + // between IDE and console. |
| 136 | + if (record.EventType != Kernel32.EVENT_TYPE.FOCUS_EVENT) |
| 137 | + { |
| 138 | + // I assume success adding records - this is not so critical |
| 139 | + // if it is critical to you, loop on this with a miniscule delay |
| 140 | + _nonKeyEvents.TryAdd(record); |
| 141 | + } |
135 | 142 | } |
136 | 143 | } |
| 144 | + bytesRead = charsRead * BytesPerWChar; |
| 145 | + } |
| 146 | + else |
| 147 | + { |
| 148 | + Debug.Assert(bytesRead == 0, "bytesRead == 0"); |
137 | 149 | } |
138 | 150 |
|
139 | | - bytesRead = charsRead * BytesPerWChar; |
140 | | - |
141 | | - // we should continue to block if no chars read (KEY_EVENT) |
142 | | - // even though non-key events were dispatched |
143 | | - if (bytesRead == 0) goto waitForInput; |
144 | | - } |
145 | | - else |
146 | | - { |
147 | | - Debug.Assert(bytesRead == 0, "bytesRead == 0"); |
148 | | - } |
149 | | - |
| 151 | + } while (bytesRead == 0); |
| 152 | + |
150 | 153 | Debug.WriteLine("Read {0} character(s)", charsRead); |
| 154 | + |
151 | 155 | ret = Win32Error.ERROR_SUCCESS; |
152 | 156 | } |
153 | 157 |
|
|
0 commit comments