7b67130947547af9a84b0f7b5f8a5008d1bdd801
[reactos.git] / win32ss / user / winsrv / consrv / condrv / coninput.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Driver DLL
4 * FILE: win32ss/user/winsrv/consrv/condrv/coninput.c
5 * PURPOSE: Console Input functions
6 * PROGRAMMERS: Jeffrey Morlan
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #include <consrv.h>
13
14 #define NDEBUG
15 #include <debug.h>
16
17 /* GLOBALS ********************************************************************/
18
19 #define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
20 WideCharToMultiByte((Console)->InputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
21
22 #define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
23 MultiByteToWideChar((Console)->InputCodePage, 0, (sChar), 1, (dWChar), 1)
24
25 typedef struct ConsoleInput_t
26 {
27 LIST_ENTRY ListEntry;
28 INPUT_RECORD InputEvent;
29 } ConsoleInput;
30
31
32 /* PRIVATE FUNCTIONS **********************************************************/
33
34 static VOID
35 ConioInputEventToAnsi(PCONSOLE Console, PINPUT_RECORD InputEvent)
36 {
37 if (InputEvent->EventType == KEY_EVENT)
38 {
39 WCHAR UnicodeChar = InputEvent->Event.KeyEvent.uChar.UnicodeChar;
40 InputEvent->Event.KeyEvent.uChar.UnicodeChar = 0;
41 ConsoleInputUnicodeCharToAnsiChar(Console,
42 &InputEvent->Event.KeyEvent.uChar.AsciiChar,
43 &UnicodeChar);
44 }
45 }
46
47 static VOID
48 ConioInputEventToUnicode(PCONSOLE Console, PINPUT_RECORD InputEvent)
49 {
50 if (InputEvent->EventType == KEY_EVENT)
51 {
52 CHAR AsciiChar = InputEvent->Event.KeyEvent.uChar.AsciiChar;
53 InputEvent->Event.KeyEvent.uChar.AsciiChar = 0;
54 ConsoleInputAnsiCharToUnicodeChar(Console,
55 &InputEvent->Event.KeyEvent.uChar.UnicodeChar,
56 &AsciiChar);
57 }
58 }
59
60 NTSTATUS
61 ConioAddInputEvent(PCONSOLE Console,
62 PINPUT_RECORD InputEvent,
63 BOOLEAN AppendToEnd)
64 {
65 ConsoleInput *ConInRec;
66
67 /* Check for pause or unpause */
68 if (InputEvent->EventType == KEY_EVENT && InputEvent->Event.KeyEvent.bKeyDown)
69 {
70 WORD vk = InputEvent->Event.KeyEvent.wVirtualKeyCode;
71 if (!(Console->PauseFlags & PAUSED_FROM_KEYBOARD))
72 {
73 DWORD cks = InputEvent->Event.KeyEvent.dwControlKeyState;
74 if (Console->InputBuffer.Mode & ENABLE_LINE_INPUT &&
75 (vk == VK_PAUSE || (vk == 'S' &&
76 (cks & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) &&
77 !(cks & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)))))
78 {
79 ConioPause(Console, PAUSED_FROM_KEYBOARD);
80 return STATUS_SUCCESS;
81 }
82 }
83 else
84 {
85 if ((vk < VK_SHIFT || vk > VK_CAPITAL) && vk != VK_LWIN &&
86 vk != VK_RWIN && vk != VK_NUMLOCK && vk != VK_SCROLL)
87 {
88 ConioUnpause(Console, PAUSED_FROM_KEYBOARD);
89 return STATUS_SUCCESS;
90 }
91 }
92 }
93
94 /* Add event to the queue */
95 ConInRec = ConsoleAllocHeap(0, sizeof(ConsoleInput));
96 if (ConInRec == NULL) return STATUS_INSUFFICIENT_RESOURCES;
97
98 ConInRec->InputEvent = *InputEvent;
99
100 if (AppendToEnd)
101 {
102 /* Append the event to the end of the queue */
103 InsertTailList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry);
104 }
105 else
106 {
107 /* Append the event to the beginning of the queue */
108 InsertHeadList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry);
109 }
110
111 SetEvent(Console->InputBuffer.ActiveEvent);
112 CsrNotifyWait(&Console->ReadWaitQueue,
113 FALSE,
114 NULL,
115 NULL);
116 if (!IsListEmpty(&Console->ReadWaitQueue))
117 {
118 CsrDereferenceWait(&Console->ReadWaitQueue);
119 }
120
121 return STATUS_SUCCESS;
122 }
123
124 NTSTATUS
125 ConioProcessInputEvent(PCONSOLE Console,
126 PINPUT_RECORD InputEvent)
127 {
128 return ConioAddInputEvent(Console, InputEvent, TRUE);
129 }
130
131 VOID
132 PurgeInputBuffer(PCONSOLE Console)
133 {
134 PLIST_ENTRY CurrentEntry;
135 ConsoleInput* Event;
136
137 while (!IsListEmpty(&Console->InputBuffer.InputEvents))
138 {
139 CurrentEntry = RemoveHeadList(&Console->InputBuffer.InputEvents);
140 Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
141 ConsoleFreeHeap(Event);
142 }
143
144 CloseHandle(Console->InputBuffer.ActiveEvent);
145 }
146
147
148 /* PUBLIC DRIVER APIS *********************************************************/
149
150 NTSTATUS NTAPI
151 ConDrvReadConsole(IN PCONSOLE Console,
152 IN PCONSOLE_INPUT_BUFFER InputBuffer,
153 IN BOOLEAN Unicode,
154 OUT PVOID Buffer,
155 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
156 IN ULONG NumCharsToRead,
157 OUT PULONG NumCharsRead OPTIONAL)
158 {
159 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
160 NTSTATUS Status = STATUS_PENDING;
161 PLIST_ENTRY CurrentEntry;
162 ConsoleInput *Input;
163 ULONG i;
164
165 if (Console == NULL || InputBuffer == NULL || /* Buffer == NULL || */
166 ReadControl == NULL || ReadControl->nLength != sizeof(CONSOLE_READCONSOLE_CONTROL))
167 {
168 return STATUS_INVALID_PARAMETER;
169 }
170
171 /* Validity checks */
172 ASSERT(Console == InputBuffer->Header.Console);
173 ASSERT( (Buffer != NULL && NumCharsToRead > 0) ||
174 (Buffer == NULL && NumCharsToRead == 0) );
175
176 /* We haven't read anything (yet) */
177
178 i = ReadControl->nInitialChars;
179
180 if (InputBuffer->Mode & ENABLE_LINE_INPUT)
181 {
182 if (Console->LineBuffer == NULL)
183 {
184 /* Starting a new line */
185 Console->LineMaxSize = (WORD)max(256, NumCharsToRead);
186
187 Console->LineBuffer = ConsoleAllocHeap(0, Console->LineMaxSize * sizeof(WCHAR));
188 if (Console->LineBuffer == NULL) return STATUS_NO_MEMORY;
189
190 Console->LineComplete = FALSE;
191 Console->LineUpPressed = FALSE;
192 Console->LineInsertToggle = Console->InsertMode;
193 Console->LineWakeupMask = ReadControl->dwCtrlWakeupMask;
194 Console->LineSize = ReadControl->nInitialChars;
195 Console->LinePos = Console->LineSize;
196
197 /*
198 * Pre-filling the buffer is only allowed in the Unicode API,
199 * so we don't need to worry about ANSI <-> Unicode conversion.
200 */
201 memcpy(Console->LineBuffer, Buffer, Console->LineSize * sizeof(WCHAR));
202 if (Console->LineSize == Console->LineMaxSize)
203 {
204 Console->LineComplete = TRUE;
205 Console->LinePos = 0;
206 }
207 }
208
209 /* If we don't have a complete line yet, process the pending input */
210 while (!Console->LineComplete && !IsListEmpty(&InputBuffer->InputEvents))
211 {
212 /* Remove input event from queue */
213 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
214 if (IsListEmpty(&InputBuffer->InputEvents))
215 {
216 ResetEvent(InputBuffer->ActiveEvent);
217 }
218 Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
219
220 /* Only pay attention to key down */
221 if (Input->InputEvent.EventType == KEY_EVENT &&
222 Input->InputEvent.Event.KeyEvent.bKeyDown)
223 {
224 LineInputKeyDown(Console, &Input->InputEvent.Event.KeyEvent);
225 ReadControl->dwControlKeyState = Input->InputEvent.Event.KeyEvent.dwControlKeyState;
226 }
227 ConsoleFreeHeap(Input);
228 }
229
230 /* Check if we have a complete line to read from */
231 if (Console->LineComplete)
232 {
233 while (i < NumCharsToRead && Console->LinePos != Console->LineSize)
234 {
235 WCHAR Char = Console->LineBuffer[Console->LinePos++];
236
237 if (Unicode)
238 {
239 ((PWCHAR)Buffer)[i] = Char;
240 }
241 else
242 {
243 ConsoleInputUnicodeCharToAnsiChar(Console, &((PCHAR)Buffer)[i], &Char);
244 }
245 ++i;
246 }
247
248 if (Console->LinePos == Console->LineSize)
249 {
250 /* Entire line has been read */
251 ConsoleFreeHeap(Console->LineBuffer);
252 Console->LineBuffer = NULL;
253 }
254
255 Status = STATUS_SUCCESS;
256 }
257 }
258 else
259 {
260 /* Character input */
261 while (i < NumCharsToRead && !IsListEmpty(&InputBuffer->InputEvents))
262 {
263 /* Remove input event from queue */
264 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
265 if (IsListEmpty(&InputBuffer->InputEvents))
266 {
267 ResetEvent(InputBuffer->ActiveEvent);
268 }
269 Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
270
271 /* Only pay attention to valid ASCII chars, on key down */
272 if (Input->InputEvent.EventType == KEY_EVENT &&
273 Input->InputEvent.Event.KeyEvent.bKeyDown &&
274 Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar != L'\0')
275 {
276 WCHAR Char = Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar;
277
278 if (Unicode)
279 {
280 ((PWCHAR)Buffer)[i] = Char;
281 }
282 else
283 {
284 ConsoleInputUnicodeCharToAnsiChar(Console, &((PCHAR)Buffer)[i], &Char);
285 }
286 ++i;
287
288 /* Did read something */
289 Status = STATUS_SUCCESS;
290 }
291 ConsoleFreeHeap(Input);
292 }
293 }
294
295 if (NumCharsRead) *NumCharsRead = i;
296
297 return Status;
298 }
299
300 NTSTATUS NTAPI
301 ConDrvGetConsoleInput(IN PCONSOLE Console,
302 IN PCONSOLE_INPUT_BUFFER InputBuffer,
303 IN BOOLEAN KeepEvents,
304 IN BOOLEAN WaitForMoreEvents,
305 IN BOOLEAN Unicode,
306 OUT PINPUT_RECORD InputRecord,
307 IN ULONG NumEventsToRead,
308 OUT PULONG NumEventsRead OPTIONAL)
309 {
310 PLIST_ENTRY CurrentInput;
311 ConsoleInput* Input;
312 ULONG i = 0;
313
314 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */)
315 return STATUS_INVALID_PARAMETER;
316
317 /* Validity checks */
318 ASSERT(Console == InputBuffer->Header.Console);
319 ASSERT( (InputRecord != NULL && NumEventsToRead > 0) ||
320 (InputRecord == NULL && NumEventsToRead == 0) );
321
322 // Do NOT do that !! Use the existing number of events already read, if any...
323 // if (NumEventsRead) *NumEventsRead = 0;
324
325 if (IsListEmpty(&InputBuffer->InputEvents))
326 {
327 /*
328 * No input is available. Wait for more input if requested,
329 * otherwise, we don't wait, so we return success.
330 */
331 return (WaitForMoreEvents ? STATUS_PENDING : STATUS_SUCCESS);
332 }
333
334 /* Only get input if there is any */
335 CurrentInput = InputBuffer->InputEvents.Flink;
336 if (NumEventsRead) i = *NumEventsRead; // We will read the remaining events...
337
338 while ((CurrentInput != &InputBuffer->InputEvents) && (i < NumEventsToRead))
339 {
340 Input = CONTAINING_RECORD(CurrentInput, ConsoleInput, ListEntry);
341
342 *InputRecord = Input->InputEvent;
343
344 if (!Unicode)
345 {
346 ConioInputEventToAnsi(InputBuffer->Header.Console, InputRecord);
347 }
348
349 ++InputRecord;
350 ++i;
351 CurrentInput = CurrentInput->Flink;
352
353 /* Remove the events from the queue if needed */
354 if (!KeepEvents)
355 {
356 RemoveEntryList(&Input->ListEntry);
357 ConsoleFreeHeap(Input);
358 }
359 }
360
361 if (NumEventsRead) *NumEventsRead = i;
362
363 if (IsListEmpty(&InputBuffer->InputEvents))
364 {
365 ResetEvent(InputBuffer->ActiveEvent);
366 }
367
368 /* We read all the inputs available, we return success */
369 return STATUS_SUCCESS;
370 }
371
372 NTSTATUS NTAPI
373 ConDrvWriteConsoleInput(IN PCONSOLE Console,
374 IN PCONSOLE_INPUT_BUFFER InputBuffer,
375 IN BOOLEAN Unicode,
376 IN BOOLEAN AppendToEnd,
377 IN PINPUT_RECORD InputRecord,
378 IN ULONG NumEventsToWrite,
379 OUT PULONG NumEventsWritten OPTIONAL)
380 {
381 NTSTATUS Status = STATUS_SUCCESS;
382 ULONG i;
383
384 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */)
385 return STATUS_INVALID_PARAMETER;
386
387 /* Validity checks */
388 ASSERT(Console == InputBuffer->Header.Console);
389 ASSERT( (InputRecord != NULL && NumEventsToWrite > 0) ||
390 (InputRecord == NULL && NumEventsToWrite == 0) );
391
392 // Do NOT do that !! Use the existing number of events already written, if any...
393 // if (NumEventsWritten) *NumEventsWritten = 0;
394
395 for (i = (NumEventsWritten ? *NumEventsWritten : 0); i < NumEventsToWrite && NT_SUCCESS(Status); ++i)
396 {
397 if (!Unicode)
398 {
399 ConioInputEventToUnicode(Console, InputRecord);
400 }
401
402 Status = ConioAddInputEvent(Console, InputRecord++, AppendToEnd);
403 }
404
405 if (NumEventsWritten) *NumEventsWritten = i;
406
407 return Status;
408 }
409
410 NTSTATUS NTAPI
411 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
412 IN PCONSOLE_INPUT_BUFFER InputBuffer)
413 {
414 PLIST_ENTRY CurrentEntry;
415 ConsoleInput* Event;
416
417 if (Console == NULL || InputBuffer == NULL)
418 return STATUS_INVALID_PARAMETER;
419
420 /* Validity check */
421 ASSERT(Console == InputBuffer->Header.Console);
422
423 /* Discard all entries in the input event queue */
424 while (!IsListEmpty(&InputBuffer->InputEvents))
425 {
426 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
427 Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
428 ConsoleFreeHeap(Event);
429 }
430 ResetEvent(InputBuffer->ActiveEvent);
431
432 return STATUS_SUCCESS;
433 }
434
435 NTSTATUS NTAPI
436 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
437 IN PCONSOLE_INPUT_BUFFER InputBuffer,
438 OUT PULONG NumberOfEvents)
439 {
440 PLIST_ENTRY CurrentInput;
441
442 if (Console == NULL || InputBuffer == NULL || NumberOfEvents == NULL)
443 return STATUS_INVALID_PARAMETER;
444
445 /* Validity check */
446 ASSERT(Console == InputBuffer->Header.Console);
447
448 *NumberOfEvents = 0;
449
450 /* If there are any events ... */
451 CurrentInput = InputBuffer->InputEvents.Flink;
452 while (CurrentInput != &InputBuffer->InputEvents)
453 {
454 CurrentInput = CurrentInput->Flink;
455 (*NumberOfEvents)++;
456 }
457
458 return STATUS_SUCCESS;
459 }
460
461 /* EOF */