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 ********************************************************************/
19 #define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
20 WideCharToMultiByte((Console)->InputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
22 #define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
23 MultiByteToWideChar((Console)->InputCodePage, 0, (sChar), 1, (dWChar), 1)
25 typedef struct ConsoleInput_t
28 INPUT_RECORD InputEvent
;
32 /* PRIVATE FUNCTIONS **********************************************************/
35 ConioInputEventToAnsi(PCONSOLE Console
, PINPUT_RECORD InputEvent
)
37 if (InputEvent
->EventType
== KEY_EVENT
)
39 WCHAR UnicodeChar
= InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
;
40 InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
= 0;
41 ConsoleInputUnicodeCharToAnsiChar(Console
,
42 &InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
,
48 ConioInputEventToUnicode(PCONSOLE Console
, PINPUT_RECORD InputEvent
)
50 if (InputEvent
->EventType
== KEY_EVENT
)
52 CHAR AsciiChar
= InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
;
53 InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
= 0;
54 ConsoleInputAnsiCharToUnicodeChar(Console
,
55 &InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
,
61 ConioAddInputEvent(PCONSOLE Console
,
62 PINPUT_RECORD InputEvent
,
65 ConsoleInput
*ConInRec
;
67 /* Check for pause or unpause */
68 if (InputEvent
->EventType
== KEY_EVENT
&& InputEvent
->Event
.KeyEvent
.bKeyDown
)
70 WORD vk
= InputEvent
->Event
.KeyEvent
.wVirtualKeyCode
;
71 if (!(Console
->PauseFlags
& PAUSED_FROM_KEYBOARD
))
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
)))))
79 ConioPause(Console
, PAUSED_FROM_KEYBOARD
);
80 return STATUS_SUCCESS
;
85 if ((vk
< VK_SHIFT
|| vk
> VK_CAPITAL
) && vk
!= VK_LWIN
&&
86 vk
!= VK_RWIN
&& vk
!= VK_NUMLOCK
&& vk
!= VK_SCROLL
)
88 ConioUnpause(Console
, PAUSED_FROM_KEYBOARD
);
89 return STATUS_SUCCESS
;
94 /* Add event to the queue */
95 ConInRec
= ConsoleAllocHeap(0, sizeof(ConsoleInput
));
96 if (ConInRec
== NULL
) return STATUS_INSUFFICIENT_RESOURCES
;
98 ConInRec
->InputEvent
= *InputEvent
;
102 /* Append the event to the end of the queue */
103 InsertTailList(&Console
->InputBuffer
.InputEvents
, &ConInRec
->ListEntry
);
107 /* Append the event to the beginning of the queue */
108 InsertHeadList(&Console
->InputBuffer
.InputEvents
, &ConInRec
->ListEntry
);
111 SetEvent(Console
->InputBuffer
.ActiveEvent
);
112 CsrNotifyWait(&Console
->ReadWaitQueue
,
116 if (!IsListEmpty(&Console
->ReadWaitQueue
))
118 CsrDereferenceWait(&Console
->ReadWaitQueue
);
121 return STATUS_SUCCESS
;
125 ConioProcessInputEvent(PCONSOLE Console
,
126 PINPUT_RECORD InputEvent
)
128 return ConioAddInputEvent(Console
, InputEvent
, TRUE
);
132 PurgeInputBuffer(PCONSOLE Console
)
134 PLIST_ENTRY CurrentEntry
;
137 while (!IsListEmpty(&Console
->InputBuffer
.InputEvents
))
139 CurrentEntry
= RemoveHeadList(&Console
->InputBuffer
.InputEvents
);
140 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
141 ConsoleFreeHeap(Event
);
144 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
148 /* PUBLIC DRIVER APIS *********************************************************/
151 ConDrvReadConsole(IN PCONSOLE Console
,
152 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
155 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl
,
156 IN ULONG NumCharsToRead
,
157 OUT PULONG NumCharsRead OPTIONAL
)
159 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
160 NTSTATUS Status
= STATUS_PENDING
;
161 PLIST_ENTRY CurrentEntry
;
165 if (Console
== NULL
|| InputBuffer
== NULL
|| /* Buffer == NULL || */
166 ReadControl
== NULL
|| ReadControl
->nLength
!= sizeof(CONSOLE_READCONSOLE_CONTROL
))
168 return STATUS_INVALID_PARAMETER
;
171 /* Validity checks */
172 ASSERT(Console
== InputBuffer
->Header
.Console
);
173 ASSERT( (Buffer
!= NULL
&& NumCharsToRead
> 0) ||
174 (Buffer
== NULL
&& NumCharsToRead
== 0) );
176 /* We haven't read anything (yet) */
178 i
= ReadControl
->nInitialChars
;
180 if (InputBuffer
->Mode
& ENABLE_LINE_INPUT
)
182 if (Console
->LineBuffer
== NULL
)
184 /* Starting a new line */
185 Console
->LineMaxSize
= (WORD
)max(256, NumCharsToRead
);
187 Console
->LineBuffer
= ConsoleAllocHeap(0, Console
->LineMaxSize
* sizeof(WCHAR
));
188 if (Console
->LineBuffer
== NULL
) return STATUS_NO_MEMORY
;
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
;
198 * Pre-filling the buffer is only allowed in the Unicode API,
199 * so we don't need to worry about ANSI <-> Unicode conversion.
201 memcpy(Console
->LineBuffer
, Buffer
, Console
->LineSize
* sizeof(WCHAR
));
202 if (Console
->LineSize
== Console
->LineMaxSize
)
204 Console
->LineComplete
= TRUE
;
205 Console
->LinePos
= 0;
209 /* If we don't have a complete line yet, process the pending input */
210 while (!Console
->LineComplete
&& !IsListEmpty(&InputBuffer
->InputEvents
))
212 /* Remove input event from queue */
213 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
214 if (IsListEmpty(&InputBuffer
->InputEvents
))
216 ResetEvent(InputBuffer
->ActiveEvent
);
218 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
220 /* Only pay attention to key down */
221 if (Input
->InputEvent
.EventType
== KEY_EVENT
&&
222 Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
)
224 LineInputKeyDown(Console
, &Input
->InputEvent
.Event
.KeyEvent
);
225 ReadControl
->dwControlKeyState
= Input
->InputEvent
.Event
.KeyEvent
.dwControlKeyState
;
227 ConsoleFreeHeap(Input
);
230 /* Check if we have a complete line to read from */
231 if (Console
->LineComplete
)
233 while (i
< NumCharsToRead
&& Console
->LinePos
!= Console
->LineSize
)
235 WCHAR Char
= Console
->LineBuffer
[Console
->LinePos
++];
239 ((PWCHAR
)Buffer
)[i
] = Char
;
243 ConsoleInputUnicodeCharToAnsiChar(Console
, &((PCHAR
)Buffer
)[i
], &Char
);
248 if (Console
->LinePos
== Console
->LineSize
)
250 /* Entire line has been read */
251 ConsoleFreeHeap(Console
->LineBuffer
);
252 Console
->LineBuffer
= NULL
;
255 Status
= STATUS_SUCCESS
;
260 /* Character input */
261 while (i
< NumCharsToRead
&& !IsListEmpty(&InputBuffer
->InputEvents
))
263 /* Remove input event from queue */
264 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
265 if (IsListEmpty(&InputBuffer
->InputEvents
))
267 ResetEvent(InputBuffer
->ActiveEvent
);
269 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
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')
276 WCHAR Char
= Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
;
280 ((PWCHAR
)Buffer
)[i
] = Char
;
284 ConsoleInputUnicodeCharToAnsiChar(Console
, &((PCHAR
)Buffer
)[i
], &Char
);
288 /* Did read something */
289 Status
= STATUS_SUCCESS
;
291 ConsoleFreeHeap(Input
);
295 if (NumCharsRead
) *NumCharsRead
= i
;
301 ConDrvGetConsoleInput(IN PCONSOLE Console
,
302 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
303 IN BOOLEAN KeepEvents
,
304 IN BOOLEAN WaitForMoreEvents
,
306 OUT PINPUT_RECORD InputRecord
,
307 IN ULONG NumEventsToRead
,
308 OUT PULONG NumEventsRead OPTIONAL
)
310 PLIST_ENTRY CurrentInput
;
314 if (Console
== NULL
|| InputBuffer
== NULL
/* || InputRecord == NULL */)
315 return STATUS_INVALID_PARAMETER
;
317 /* Validity checks */
318 ASSERT(Console
== InputBuffer
->Header
.Console
);
319 ASSERT( (InputRecord
!= NULL
&& NumEventsToRead
> 0) ||
320 (InputRecord
== NULL
&& NumEventsToRead
== 0) );
322 // Do NOT do that !! Use the existing number of events already read, if any...
323 // if (NumEventsRead) *NumEventsRead = 0;
325 if (IsListEmpty(&InputBuffer
->InputEvents
))
328 * No input is available. Wait for more input if requested,
329 * otherwise, we don't wait, so we return success.
331 return (WaitForMoreEvents
? STATUS_PENDING
: STATUS_SUCCESS
);
334 /* Only get input if there is any */
335 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
336 if (NumEventsRead
) i
= *NumEventsRead
; // We will read the remaining events...
338 while ((CurrentInput
!= &InputBuffer
->InputEvents
) && (i
< NumEventsToRead
))
340 Input
= CONTAINING_RECORD(CurrentInput
, ConsoleInput
, ListEntry
);
342 *InputRecord
= Input
->InputEvent
;
346 ConioInputEventToAnsi(InputBuffer
->Header
.Console
, InputRecord
);
351 CurrentInput
= CurrentInput
->Flink
;
353 /* Remove the events from the queue if needed */
356 RemoveEntryList(&Input
->ListEntry
);
357 ConsoleFreeHeap(Input
);
361 if (NumEventsRead
) *NumEventsRead
= i
;
363 if (IsListEmpty(&InputBuffer
->InputEvents
))
365 ResetEvent(InputBuffer
->ActiveEvent
);
368 /* We read all the inputs available, we return success */
369 return STATUS_SUCCESS
;
373 ConDrvWriteConsoleInput(IN PCONSOLE Console
,
374 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
376 IN BOOLEAN AppendToEnd
,
377 IN PINPUT_RECORD InputRecord
,
378 IN ULONG NumEventsToWrite
,
379 OUT PULONG NumEventsWritten OPTIONAL
)
381 NTSTATUS Status
= STATUS_SUCCESS
;
384 if (Console
== NULL
|| InputBuffer
== NULL
/* || InputRecord == NULL */)
385 return STATUS_INVALID_PARAMETER
;
387 /* Validity checks */
388 ASSERT(Console
== InputBuffer
->Header
.Console
);
389 ASSERT( (InputRecord
!= NULL
&& NumEventsToWrite
> 0) ||
390 (InputRecord
== NULL
&& NumEventsToWrite
== 0) );
392 // Do NOT do that !! Use the existing number of events already written, if any...
393 // if (NumEventsWritten) *NumEventsWritten = 0;
395 for (i
= (NumEventsWritten
? *NumEventsWritten
: 0); i
< NumEventsToWrite
&& NT_SUCCESS(Status
); ++i
)
399 ConioInputEventToUnicode(Console
, InputRecord
);
402 Status
= ConioAddInputEvent(Console
, InputRecord
++, AppendToEnd
);
405 if (NumEventsWritten
) *NumEventsWritten
= i
;
411 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console
,
412 IN PCONSOLE_INPUT_BUFFER InputBuffer
)
414 PLIST_ENTRY CurrentEntry
;
417 if (Console
== NULL
|| InputBuffer
== NULL
)
418 return STATUS_INVALID_PARAMETER
;
421 ASSERT(Console
== InputBuffer
->Header
.Console
);
423 /* Discard all entries in the input event queue */
424 while (!IsListEmpty(&InputBuffer
->InputEvents
))
426 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
427 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
428 ConsoleFreeHeap(Event
);
430 ResetEvent(InputBuffer
->ActiveEvent
);
432 return STATUS_SUCCESS
;
436 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console
,
437 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
438 OUT PULONG NumberOfEvents
)
440 PLIST_ENTRY CurrentInput
;
442 if (Console
== NULL
|| InputBuffer
== NULL
|| NumberOfEvents
== NULL
)
443 return STATUS_INVALID_PARAMETER
;
446 ASSERT(Console
== InputBuffer
->Header
.Console
);
450 /* If there are any events ... */
451 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
452 while (CurrentInput
!= &InputBuffer
->InputEvents
)
454 CurrentInput
= CurrentInput
->Flink
;
458 return STATUS_SUCCESS
;