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
,
69 ConioAddInputEvent(PCONSOLE Console
,
70 PINPUT_RECORD InputEvent
,
73 ConsoleInput
*ConInRec
;
75 /* Check for pause or unpause */
76 if (InputEvent
->EventType
== KEY_EVENT
&& InputEvent
->Event
.KeyEvent
.bKeyDown
)
78 WORD vk
= InputEvent
->Event
.KeyEvent
.wVirtualKeyCode
;
79 if (!(Console
->PauseFlags
& PAUSED_FROM_KEYBOARD
))
81 DWORD cks
= InputEvent
->Event
.KeyEvent
.dwControlKeyState
;
82 if (Console
->InputBuffer
.Mode
& ENABLE_LINE_INPUT
&&
83 (vk
== VK_PAUSE
|| (vk
== 'S' &&
84 (cks
& (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
)) &&
85 !(cks
& (LEFT_ALT_PRESSED
| RIGHT_ALT_PRESSED
)))))
87 ConioPause(Console
, PAUSED_FROM_KEYBOARD
);
88 return STATUS_SUCCESS
;
93 if ((vk
< VK_SHIFT
|| vk
> VK_CAPITAL
) && vk
!= VK_LWIN
&&
94 vk
!= VK_RWIN
&& vk
!= VK_NUMLOCK
&& vk
!= VK_SCROLL
)
96 ConioUnpause(Console
, PAUSED_FROM_KEYBOARD
);
97 return STATUS_SUCCESS
;
102 /* Add event to the queue */
103 ConInRec
= ConsoleAllocHeap(0, sizeof(ConsoleInput
));
104 if (ConInRec
== NULL
) return STATUS_INSUFFICIENT_RESOURCES
;
106 ConInRec
->InputEvent
= *InputEvent
;
110 /* Append the event to the end of the queue */
111 InsertTailList(&Console
->InputBuffer
.InputEvents
, &ConInRec
->ListEntry
);
115 /* Append the event to the beginning of the queue */
116 InsertHeadList(&Console
->InputBuffer
.InputEvents
, &ConInRec
->ListEntry
);
119 SetEvent(Console
->InputBuffer
.ActiveEvent
);
120 CsrNotifyWait(&Console
->ReadWaitQueue
,
124 if (!IsListEmpty(&Console
->ReadWaitQueue
))
126 CsrDereferenceWait(&Console
->ReadWaitQueue
);
129 return STATUS_SUCCESS
;
133 ConioProcessInputEvent(PCONSOLE Console
,
134 PINPUT_RECORD InputEvent
)
136 return ConioAddInputEvent(Console
, InputEvent
, TRUE
);
140 PurgeInputBuffer(PCONSOLE Console
)
142 PLIST_ENTRY CurrentEntry
;
145 while (!IsListEmpty(&Console
->InputBuffer
.InputEvents
))
147 CurrentEntry
= RemoveHeadList(&Console
->InputBuffer
.InputEvents
);
148 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
149 ConsoleFreeHeap(Event
);
152 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
156 /* PUBLIC DRIVER APIS *********************************************************/
159 ConDrvReadConsole(IN PCONSOLE Console
,
160 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
163 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl
,
164 IN ULONG NumCharsToRead
,
165 OUT PULONG NumCharsRead OPTIONAL
)
167 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
168 NTSTATUS Status
= STATUS_PENDING
;
169 PLIST_ENTRY CurrentEntry
;
173 if (Console
== NULL
|| InputBuffer
== NULL
|| /* Buffer == NULL || */
174 ReadControl
== NULL
|| ReadControl
->nLength
!= sizeof(CONSOLE_READCONSOLE_CONTROL
))
176 return STATUS_INVALID_PARAMETER
;
179 /* Validity checks */
180 ASSERT(Console
== InputBuffer
->Header
.Console
);
181 ASSERT((Buffer
!= NULL
) || (Buffer
== NULL
&& NumCharsToRead
== 0));
183 /* We haven't read anything (yet) */
185 i
= ReadControl
->nInitialChars
;
187 if (InputBuffer
->Mode
& ENABLE_LINE_INPUT
)
189 if (Console
->LineBuffer
== NULL
)
191 /* Starting a new line */
192 Console
->LineMaxSize
= (WORD
)max(256, NumCharsToRead
);
194 Console
->LineBuffer
= ConsoleAllocHeap(0, Console
->LineMaxSize
* sizeof(WCHAR
));
195 if (Console
->LineBuffer
== NULL
) return STATUS_NO_MEMORY
;
197 Console
->LineComplete
= FALSE
;
198 Console
->LineUpPressed
= FALSE
;
199 Console
->LineInsertToggle
= Console
->InsertMode
;
200 Console
->LineWakeupMask
= ReadControl
->dwCtrlWakeupMask
;
201 Console
->LineSize
= ReadControl
->nInitialChars
;
202 Console
->LinePos
= Console
->LineSize
;
205 * Pre-filling the buffer is only allowed in the Unicode API,
206 * so we don't need to worry about ANSI <-> Unicode conversion.
208 memcpy(Console
->LineBuffer
, Buffer
, Console
->LineSize
* sizeof(WCHAR
));
209 if (Console
->LineSize
== Console
->LineMaxSize
)
211 Console
->LineComplete
= TRUE
;
212 Console
->LinePos
= 0;
216 /* If we don't have a complete line yet, process the pending input */
217 while (!Console
->LineComplete
&& !IsListEmpty(&InputBuffer
->InputEvents
))
219 /* Remove input event from queue */
220 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
221 if (IsListEmpty(&InputBuffer
->InputEvents
))
223 ResetEvent(InputBuffer
->ActiveEvent
);
225 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
227 /* Only pay attention to key down */
228 if (Input
->InputEvent
.EventType
== KEY_EVENT
&&
229 Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
)
231 LineInputKeyDown(Console
, &Input
->InputEvent
.Event
.KeyEvent
);
232 ReadControl
->dwControlKeyState
= Input
->InputEvent
.Event
.KeyEvent
.dwControlKeyState
;
234 ConsoleFreeHeap(Input
);
237 /* Check if we have a complete line to read from */
238 if (Console
->LineComplete
)
240 while (i
< NumCharsToRead
&& Console
->LinePos
!= Console
->LineSize
)
242 WCHAR Char
= Console
->LineBuffer
[Console
->LinePos
++];
246 ((PWCHAR
)Buffer
)[i
] = Char
;
250 ConsoleInputUnicodeCharToAnsiChar(Console
, &((PCHAR
)Buffer
)[i
], &Char
);
255 if (Console
->LinePos
== Console
->LineSize
)
257 /* Entire line has been read */
258 ConsoleFreeHeap(Console
->LineBuffer
);
259 Console
->LineBuffer
= NULL
;
262 Status
= STATUS_SUCCESS
;
267 /* Character input */
268 while (i
< NumCharsToRead
&& !IsListEmpty(&InputBuffer
->InputEvents
))
270 /* Remove input event from queue */
271 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
272 if (IsListEmpty(&InputBuffer
->InputEvents
))
274 ResetEvent(InputBuffer
->ActiveEvent
);
276 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
278 /* Only pay attention to valid ASCII chars, on key down */
279 if (Input
->InputEvent
.EventType
== KEY_EVENT
&&
280 Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
&&
281 Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
!= L
'\0')
283 WCHAR Char
= Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
;
287 ((PWCHAR
)Buffer
)[i
] = Char
;
291 ConsoleInputUnicodeCharToAnsiChar(Console
, &((PCHAR
)Buffer
)[i
], &Char
);
295 /* Did read something */
296 Status
= STATUS_SUCCESS
;
298 ConsoleFreeHeap(Input
);
302 if (NumCharsRead
) *NumCharsRead
= i
;
308 ConDrvGetConsoleInput(IN PCONSOLE Console
,
309 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
310 IN BOOLEAN KeepEvents
,
311 IN BOOLEAN WaitForMoreEvents
,
313 OUT PINPUT_RECORD InputRecord
,
314 IN ULONG NumEventsToRead
,
315 OUT PULONG NumEventsRead OPTIONAL
)
317 PLIST_ENTRY CurrentInput
;
321 if (Console
== NULL
|| InputBuffer
== NULL
/* || InputRecord == NULL */)
322 return STATUS_INVALID_PARAMETER
;
324 /* Validity checks */
325 ASSERT(Console
== InputBuffer
->Header
.Console
);
326 ASSERT((InputRecord
!= NULL
) || (InputRecord
== NULL
&& NumEventsToRead
== 0));
328 if (NumEventsRead
) *NumEventsRead
= 0;
330 if (IsListEmpty(&InputBuffer
->InputEvents
))
333 * No input is available. Wait for more input if requested,
334 * otherwise, we don't wait, so we return success.
336 return (WaitForMoreEvents
? STATUS_PENDING
: STATUS_SUCCESS
);
339 /* Only get input if there is any */
340 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
342 while ((CurrentInput
!= &InputBuffer
->InputEvents
) && (i
< NumEventsToRead
))
344 Input
= CONTAINING_RECORD(CurrentInput
, ConsoleInput
, ListEntry
);
346 *InputRecord
= Input
->InputEvent
;
350 ConioInputEventToAnsi(InputBuffer
->Header
.Console
, InputRecord
);
355 CurrentInput
= CurrentInput
->Flink
;
357 /* Remove the events from the queue if needed */
360 RemoveEntryList(&Input
->ListEntry
);
361 ConsoleFreeHeap(Input
);
365 if (NumEventsRead
) *NumEventsRead
= i
;
367 if (IsListEmpty(&InputBuffer
->InputEvents
))
369 ResetEvent(InputBuffer
->ActiveEvent
);
372 /* We read all the inputs available, we return success */
373 return STATUS_SUCCESS
;
377 ConDrvWriteConsoleInput(IN PCONSOLE Console
,
378 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
380 IN BOOLEAN AppendToEnd
,
381 IN PINPUT_RECORD InputRecord
,
382 IN ULONG NumEventsToWrite
,
383 OUT PULONG NumEventsWritten OPTIONAL
)
385 NTSTATUS Status
= STATUS_SUCCESS
;
388 if (Console
== NULL
|| InputBuffer
== NULL
/* || InputRecord == NULL */)
389 return STATUS_INVALID_PARAMETER
;
391 /* Validity checks */
392 ASSERT(Console
== InputBuffer
->Header
.Console
);
393 ASSERT((InputRecord
!= NULL
) || (InputRecord
== NULL
&& NumEventsToWrite
== 0));
395 // if (NumEventsWritten) *NumEventsWritten = 0;
396 // Status = ConioAddInputEvents(Console, InputRecord, NumEventsToWrite, NumEventsWritten, AppendToEnd);
398 for (i
= 0; i
< NumEventsToWrite
&& NT_SUCCESS(Status
); ++i
)
402 ConioInputEventToUnicode(Console
, InputRecord
);
405 Status
= ConioAddInputEvent(Console
, InputRecord
++, AppendToEnd
);
408 if (NumEventsWritten
) *NumEventsWritten
= i
;
414 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console
,
415 IN PCONSOLE_INPUT_BUFFER InputBuffer
)
417 PLIST_ENTRY CurrentEntry
;
420 if (Console
== NULL
|| InputBuffer
== NULL
)
421 return STATUS_INVALID_PARAMETER
;
424 ASSERT(Console
== InputBuffer
->Header
.Console
);
426 /* Discard all entries in the input event queue */
427 while (!IsListEmpty(&InputBuffer
->InputEvents
))
429 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
430 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
431 ConsoleFreeHeap(Event
);
433 ResetEvent(InputBuffer
->ActiveEvent
);
435 return STATUS_SUCCESS
;
439 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console
,
440 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
441 OUT PULONG NumberOfEvents
)
443 PLIST_ENTRY CurrentInput
;
445 if (Console
== NULL
|| InputBuffer
== NULL
|| NumberOfEvents
== NULL
)
446 return STATUS_INVALID_PARAMETER
;
449 ASSERT(Console
== InputBuffer
->Header
.Console
);
453 /* If there are any events ... */
454 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
455 while (CurrentInput
!= &InputBuffer
->InputEvents
)
457 CurrentInput
= CurrentInput
->Flink
;
461 return STATUS_SUCCESS
;