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)
10 /* INCLUDES *******************************************************************/
17 /* GLOBALS ********************************************************************/
21 * "The lpMultiByteStr and lpWideCharStr pointers must not be the same.
22 * If they are the same, the function fails, and GetLastError returns
23 * ERROR_INVALID_PARAMETER."
25 #define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
26 ASSERT((ULONG_PTR)dChar != (ULONG_PTR)sWChar); \
27 WideCharToMultiByte((Console)->InputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
29 #define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
30 ASSERT((ULONG_PTR)dWChar != (ULONG_PTR)sChar); \
31 MultiByteToWideChar((Console)->InputCodePage, 0, (sChar), 1, (dWChar), 1)
33 typedef struct ConsoleInput_t
36 INPUT_RECORD InputEvent
;
40 /* PRIVATE FUNCTIONS **********************************************************/
43 ConioInputEventToAnsi(PCONSOLE Console
, PINPUT_RECORD InputEvent
)
45 if (InputEvent
->EventType
== KEY_EVENT
)
47 WCHAR UnicodeChar
= InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
;
48 InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
= 0;
49 ConsoleInputUnicodeCharToAnsiChar(Console
,
50 &InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
,
56 ConioInputEventToUnicode(PCONSOLE Console
, PINPUT_RECORD InputEvent
)
58 if (InputEvent
->EventType
== KEY_EVENT
)
60 CHAR AsciiChar
= InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
;
61 InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
= 0;
62 ConsoleInputAnsiCharToUnicodeChar(Console
,
63 &InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
,
70 ConDrvAddInputEvents(PCONSOLE Console
,
71 PINPUT_RECORD InputRecords
, // InputEvent
72 ULONG NumEventsToWrite
,
73 PULONG NumEventsWritten
,
76 NTSTATUS Status
= STATUS_SUCCESS
;
78 BOOLEAN SetWaitEvent
= FALSE
;
80 if (NumEventsWritten
) *NumEventsWritten
= 0;
83 * When adding many single events, in the case of repeated mouse move or
84 * key down events, we try to coalesce them so that we do not saturate
85 * too quickly the input buffer.
87 if (NumEventsToWrite
== 1 && !IsListEmpty(&Console
->InputBuffer
.InputEvents
))
89 PINPUT_RECORD InputRecord
= InputRecords
; // Only one element
90 PINPUT_RECORD LastInputRecord
;
91 ConsoleInput
* ConInRec
; // Input
93 /* Get the "next" event of the input buffer */
96 /* Get the tail element */
97 ConInRec
= CONTAINING_RECORD(Console
->InputBuffer
.InputEvents
.Blink
,
98 ConsoleInput
, ListEntry
);
102 /* Get the head element */
103 ConInRec
= CONTAINING_RECORD(Console
->InputBuffer
.InputEvents
.Flink
,
104 ConsoleInput
, ListEntry
);
106 LastInputRecord
= &ConInRec
->InputEvent
;
108 if (InputRecord
->EventType
== MOUSE_EVENT
&&
109 InputRecord
->Event
.MouseEvent
.dwEventFlags
== MOUSE_MOVED
)
111 if (LastInputRecord
->EventType
== MOUSE_EVENT
&&
112 LastInputRecord
->Event
.MouseEvent
.dwEventFlags
== MOUSE_MOVED
)
114 /* Update the mouse position */
115 LastInputRecord
->Event
.MouseEvent
.dwMousePosition
.X
=
116 InputRecord
->Event
.MouseEvent
.dwMousePosition
.X
;
117 LastInputRecord
->Event
.MouseEvent
.dwMousePosition
.Y
=
118 InputRecord
->Event
.MouseEvent
.dwMousePosition
.Y
;
121 // return STATUS_SUCCESS;
122 Status
= STATUS_SUCCESS
;
125 else if (InputRecord
->EventType
== KEY_EVENT
&&
126 InputRecord
->Event
.KeyEvent
.bKeyDown
)
128 if (LastInputRecord
->EventType
== KEY_EVENT
&&
129 LastInputRecord
->Event
.KeyEvent
.bKeyDown
&&
130 (LastInputRecord
->Event
.KeyEvent
.wVirtualScanCode
== // Same scancode
131 InputRecord
->Event
.KeyEvent
.wVirtualScanCode
) &&
132 (LastInputRecord
->Event
.KeyEvent
.uChar
.UnicodeChar
== // Same character
133 InputRecord
->Event
.KeyEvent
.uChar
.UnicodeChar
) &&
134 (LastInputRecord
->Event
.KeyEvent
.dwControlKeyState
== // Same Ctrl/Alt/Shift state
135 InputRecord
->Event
.KeyEvent
.dwControlKeyState
) )
137 /* Update the repeat count */
138 LastInputRecord
->Event
.KeyEvent
.wRepeatCount
+=
139 InputRecord
->Event
.KeyEvent
.wRepeatCount
;
142 // return STATUS_SUCCESS;
143 Status
= STATUS_SUCCESS
;
148 /* If we coalesced the only one element, we can quit */
149 if (i
== 1 && Status
== STATUS_SUCCESS
/* && NumEventsToWrite == 1 */)
153 * No event coalesced, add them in the usual way.
158 /* Go to the beginning of the list */
159 // InputRecords = InputRecords;
163 /* Go to the end of the list */
164 InputRecords
= &InputRecords
[NumEventsToWrite
- 1];
167 /* Set the event if the list is going to be non-empty */
168 if (IsListEmpty(&Console
->InputBuffer
.InputEvents
))
171 for (i
= 0; i
< NumEventsToWrite
&& NT_SUCCESS(Status
); ++i
)
173 PINPUT_RECORD InputRecord
;
174 ConsoleInput
* ConInRec
;
178 /* Select the event and go to the next one */
179 InputRecord
= InputRecords
++;
183 /* Select the event and go to the previous one */
184 InputRecord
= InputRecords
--;
187 /* Add event to the queue */
188 ConInRec
= ConsoleAllocHeap(0, sizeof(ConsoleInput
));
189 if (ConInRec
== NULL
)
191 // return STATUS_INSUFFICIENT_RESOURCES;
192 Status
= STATUS_INSUFFICIENT_RESOURCES
;
196 ConInRec
->InputEvent
= *InputRecord
;
200 /* Append the event to the end of the queue */
201 InsertTailList(&Console
->InputBuffer
.InputEvents
, &ConInRec
->ListEntry
);
205 /* Append the event to the beginning of the queue */
206 InsertHeadList(&Console
->InputBuffer
.InputEvents
, &ConInRec
->ListEntry
);
209 // return STATUS_SUCCESS;
210 Status
= STATUS_SUCCESS
;
213 if (SetWaitEvent
) SetEvent(Console
->InputBuffer
.ActiveEvent
);
216 if (NumEventsWritten
) *NumEventsWritten
= i
;
223 PreprocessInput(PCONSOLE Console
,
224 PINPUT_RECORD InputEvent
,
225 ULONG NumEventsToWrite
);
227 PostprocessInput(PCONSOLE Console
);
230 ConioAddInputEvents(PCONSOLE Console
,
231 PINPUT_RECORD InputRecords
, // InputEvent
232 ULONG NumEventsToWrite
,
233 PULONG NumEventsWritten
,
236 NTSTATUS Status
= STATUS_SUCCESS
;
238 if (NumEventsWritten
) *NumEventsWritten
= 0;
241 * This pre-processing code MUST be IN consrv ONLY!!
243 NumEventsToWrite
= PreprocessInput(Console
, InputRecords
, NumEventsToWrite
);
244 if (NumEventsToWrite
== 0) return STATUS_SUCCESS
;
246 Status
= ConDrvAddInputEvents(Console
,
253 * This post-processing code MUST be IN consrv ONLY!!
255 // if (NT_SUCCESS(Status))
256 if (Status
== STATUS_SUCCESS
) PostprocessInput(Console
);
261 /* Move elsewhere...*/
263 ConioProcessInputEvent(PCONSOLE Console
,
264 PINPUT_RECORD InputEvent
)
266 ULONG NumEventsWritten
;
267 return ConioAddInputEvents(Console
,
276 PurgeInputBuffer(PCONSOLE Console
)
278 PLIST_ENTRY CurrentEntry
;
281 while (!IsListEmpty(&Console
->InputBuffer
.InputEvents
))
283 CurrentEntry
= RemoveHeadList(&Console
->InputBuffer
.InputEvents
);
284 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
285 ConsoleFreeHeap(Event
);
288 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
292 /* PUBLIC DRIVER APIS *********************************************************/
295 ConDrvReadConsole(IN PCONSOLE Console
,
296 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
297 /**/IN PUNICODE_STRING ExeName
/**/OPTIONAL
/**/,/**/
300 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl
,
301 IN ULONG NumCharsToRead
,
302 OUT PULONG NumCharsRead OPTIONAL
)
304 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
305 NTSTATUS Status
= STATUS_PENDING
;
306 PLIST_ENTRY CurrentEntry
;
310 if (Console
== NULL
|| InputBuffer
== NULL
|| /* Buffer == NULL || */
311 ReadControl
== NULL
|| ReadControl
->nLength
!= sizeof(CONSOLE_READCONSOLE_CONTROL
))
313 return STATUS_INVALID_PARAMETER
;
316 /* Validity checks */
317 ASSERT(Console
== InputBuffer
->Header
.Console
);
318 ASSERT((Buffer
!= NULL
) || (Buffer
== NULL
&& NumCharsToRead
== 0));
320 /* We haven't read anything (yet) */
322 i
= ReadControl
->nInitialChars
;
324 if (InputBuffer
->Mode
& ENABLE_LINE_INPUT
)
326 if (Console
->LineBuffer
== NULL
)
328 /* Starting a new line */
329 Console
->LineMaxSize
= max(256, NumCharsToRead
);
331 Console
->LineBuffer
= ConsoleAllocHeap(0, Console
->LineMaxSize
* sizeof(WCHAR
));
332 if (Console
->LineBuffer
== NULL
) return STATUS_NO_MEMORY
;
334 Console
->LinePos
= Console
->LineSize
= ReadControl
->nInitialChars
;
335 Console
->LineComplete
= Console
->LineUpPressed
= FALSE
;
336 Console
->LineInsertToggle
= Console
->InsertMode
;
337 Console
->LineWakeupMask
= ReadControl
->dwCtrlWakeupMask
;
340 * Pre-filling the buffer is only allowed in the Unicode API,
341 * so we don't need to worry about ANSI <-> Unicode conversion.
343 memcpy(Console
->LineBuffer
, Buffer
, Console
->LineSize
* sizeof(WCHAR
));
344 if (Console
->LineSize
== Console
->LineMaxSize
)
346 Console
->LineComplete
= TRUE
;
347 Console
->LinePos
= 0;
351 /* If we don't have a complete line yet, process the pending input */
352 while (!Console
->LineComplete
&& !IsListEmpty(&InputBuffer
->InputEvents
))
354 /* Remove input event from queue */
355 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
356 if (IsListEmpty(&InputBuffer
->InputEvents
))
358 ResetEvent(InputBuffer
->ActiveEvent
);
360 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
362 /* Only pay attention to key down */
363 if (Input
->InputEvent
.EventType
== KEY_EVENT
&&
364 Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
)
366 LineInputKeyDown(Console
, ExeName
,
367 &Input
->InputEvent
.Event
.KeyEvent
);
368 ReadControl
->dwControlKeyState
= Input
->InputEvent
.Event
.KeyEvent
.dwControlKeyState
;
370 ConsoleFreeHeap(Input
);
373 /* Check if we have a complete line to read from */
374 if (Console
->LineComplete
)
376 while (i
< NumCharsToRead
&& Console
->LinePos
!= Console
->LineSize
)
378 WCHAR Char
= Console
->LineBuffer
[Console
->LinePos
++];
382 ((PWCHAR
)Buffer
)[i
] = Char
;
386 ConsoleInputUnicodeCharToAnsiChar(Console
, &((PCHAR
)Buffer
)[i
], &Char
);
391 if (Console
->LinePos
== Console
->LineSize
)
393 /* Entire line has been read */
394 ConsoleFreeHeap(Console
->LineBuffer
);
395 Console
->LineBuffer
= NULL
;
398 Status
= STATUS_SUCCESS
;
403 /* Character input */
404 while (i
< NumCharsToRead
&& !IsListEmpty(&InputBuffer
->InputEvents
))
406 /* Remove input event from queue */
407 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
408 if (IsListEmpty(&InputBuffer
->InputEvents
))
410 ResetEvent(InputBuffer
->ActiveEvent
);
412 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
414 /* Only pay attention to valid ASCII chars, on key down */
415 if (Input
->InputEvent
.EventType
== KEY_EVENT
&&
416 Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
&&
417 Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
!= L
'\0')
419 WCHAR Char
= Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
;
423 ((PWCHAR
)Buffer
)[i
] = Char
;
427 ConsoleInputUnicodeCharToAnsiChar(Console
, &((PCHAR
)Buffer
)[i
], &Char
);
431 /* Did read something */
432 Status
= STATUS_SUCCESS
;
434 ConsoleFreeHeap(Input
);
438 // FIXME: Only set if Status == STATUS_SUCCESS ???
439 if (NumCharsRead
) *NumCharsRead
= i
;
445 ConDrvGetConsoleInput(IN PCONSOLE Console
,
446 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
447 IN BOOLEAN KeepEvents
,
448 IN BOOLEAN WaitForMoreEvents
,
450 OUT PINPUT_RECORD InputRecord
,
451 IN ULONG NumEventsToRead
,
452 OUT PULONG NumEventsRead OPTIONAL
)
454 PLIST_ENTRY CurrentInput
;
458 if (Console
== NULL
|| InputBuffer
== NULL
/* || InputRecord == NULL */)
459 return STATUS_INVALID_PARAMETER
;
461 /* Validity checks */
462 ASSERT(Console
== InputBuffer
->Header
.Console
);
463 ASSERT((InputRecord
!= NULL
) || (InputRecord
== NULL
&& NumEventsToRead
== 0));
465 if (NumEventsRead
) *NumEventsRead
= 0;
467 if (IsListEmpty(&InputBuffer
->InputEvents
))
470 * No input is available. Wait for more input if requested,
471 * otherwise, we don't wait, so we return success.
473 return (WaitForMoreEvents
? STATUS_PENDING
: STATUS_SUCCESS
);
476 /* Only get input if there is any */
477 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
479 while ((CurrentInput
!= &InputBuffer
->InputEvents
) && (i
< NumEventsToRead
))
481 Input
= CONTAINING_RECORD(CurrentInput
, ConsoleInput
, ListEntry
);
483 *InputRecord
= Input
->InputEvent
;
487 CurrentInput
= CurrentInput
->Flink
;
489 /* Remove the events from the queue if needed */
492 RemoveEntryList(&Input
->ListEntry
);
493 ConsoleFreeHeap(Input
);
497 if (NumEventsRead
) *NumEventsRead
= i
;
499 /* Now translate everything to ANSI */
504 ConioInputEventToAnsi(InputBuffer
->Header
.Console
, --InputRecord
);
508 if (IsListEmpty(&InputBuffer
->InputEvents
))
510 ResetEvent(InputBuffer
->ActiveEvent
);
513 /* We read all the inputs available, we return success */
514 return STATUS_SUCCESS
;
518 ConDrvWriteConsoleInput(IN PCONSOLE Console
,
519 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
521 IN BOOLEAN AppendToEnd
,
522 IN PINPUT_RECORD InputRecord
,
523 IN ULONG NumEventsToWrite
,
524 OUT PULONG NumEventsWritten OPTIONAL
)
526 NTSTATUS Status
= STATUS_SUCCESS
;
529 if (Console
== NULL
|| InputBuffer
== NULL
/* || InputRecord == NULL */)
530 return STATUS_INVALID_PARAMETER
;
532 /* Validity checks */
533 ASSERT(Console
== InputBuffer
->Header
.Console
);
534 ASSERT((InputRecord
!= NULL
) || (InputRecord
== NULL
&& NumEventsToWrite
== 0));
536 /* First translate everything to UNICODE */
539 for (i
= 0; i
< NumEventsToWrite
; ++i
)
541 ConioInputEventToUnicode(Console
, &InputRecord
[i
]);
545 /* Now, add the events */
546 // if (NumEventsWritten) *NumEventsWritten = 0;
547 // ConDrvAddInputEvents
548 Status
= ConioAddInputEvents(Console
,
553 // if (NumEventsWritten) *NumEventsWritten = i;
559 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console
,
560 IN PCONSOLE_INPUT_BUFFER InputBuffer
)
562 PLIST_ENTRY CurrentEntry
;
565 if (Console
== NULL
|| InputBuffer
== NULL
)
566 return STATUS_INVALID_PARAMETER
;
569 ASSERT(Console
== InputBuffer
->Header
.Console
);
571 /* Discard all entries in the input event queue */
572 while (!IsListEmpty(&InputBuffer
->InputEvents
))
574 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
575 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
576 ConsoleFreeHeap(Event
);
578 ResetEvent(InputBuffer
->ActiveEvent
);
580 return STATUS_SUCCESS
;
584 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console
,
585 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
586 OUT PULONG NumberOfEvents
)
588 PLIST_ENTRY CurrentInput
;
590 if (Console
== NULL
|| InputBuffer
== NULL
|| NumberOfEvents
== NULL
)
591 return STATUS_INVALID_PARAMETER
;
594 ASSERT(Console
== InputBuffer
->Header
.Console
);
598 /* If there are any events ... */
599 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
600 while (CurrentInput
!= &InputBuffer
->InputEvents
)
602 CurrentInput
= CurrentInput
->Flink
;
606 return STATUS_SUCCESS
;