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 ConSrvGetInputBuffer(ProcessData, Handle, Ptr, Access, LockConsole) \
20 ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), NULL, \
21 (Access), (LockConsole), INPUT_BUFFER)
22 #define ConSrvGetInputBufferAndHandleEntry(ProcessData, Handle, Ptr, Entry, Access, LockConsole) \
23 ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), (Entry), \
24 (Access), (LockConsole), INPUT_BUFFER)
25 #define ConSrvReleaseInputBuffer(Buff, IsConsoleLocked) \
26 ConSrvReleaseObject(&(Buff)->Header, (IsConsoleLocked))
29 #define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
30 WideCharToMultiByte((Console)->CodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
32 #define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
33 MultiByteToWideChar((Console)->CodePage, 0, (sChar), 1, (dWChar), 1)
35 typedef struct ConsoleInput_t
38 INPUT_RECORD InputEvent
;
42 /* PRIVATE FUNCTIONS **********************************************************/
45 ConioInputEventToAnsi(PCONSOLE Console
, PINPUT_RECORD InputEvent
)
47 if (InputEvent
->EventType
== KEY_EVENT
)
49 WCHAR UnicodeChar
= InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
;
50 InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
= 0;
51 ConsoleInputUnicodeCharToAnsiChar(Console
,
52 &InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
,
58 ConioInputEventToUnicode(PCONSOLE Console
, PINPUT_RECORD InputEvent
)
60 if (InputEvent
->EventType
== KEY_EVENT
)
62 CHAR AsciiChar
= InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
;
63 InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
= 0;
64 ConsoleInputAnsiCharToUnicodeChar(Console
,
65 &InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
,
71 ConioAddInputEvent(PCONSOLE Console
,
72 PINPUT_RECORD InputEvent
,
75 ConsoleInput
*ConInRec
;
77 /* Check for pause or unpause */
78 if (InputEvent
->EventType
== KEY_EVENT
&& InputEvent
->Event
.KeyEvent
.bKeyDown
)
80 WORD vk
= InputEvent
->Event
.KeyEvent
.wVirtualKeyCode
;
81 if (!(Console
->PauseFlags
& PAUSED_FROM_KEYBOARD
))
83 DWORD cks
= InputEvent
->Event
.KeyEvent
.dwControlKeyState
;
84 if (Console
->InputBuffer
.Mode
& ENABLE_LINE_INPUT
&&
85 (vk
== VK_PAUSE
|| (vk
== 'S' &&
86 (cks
& (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
)) &&
87 !(cks
& (LEFT_ALT_PRESSED
| RIGHT_ALT_PRESSED
)))))
89 ConioPause(Console
, PAUSED_FROM_KEYBOARD
);
90 return STATUS_SUCCESS
;
95 if ((vk
< VK_SHIFT
|| vk
> VK_CAPITAL
) && vk
!= VK_LWIN
&&
96 vk
!= VK_RWIN
&& vk
!= VK_NUMLOCK
&& vk
!= VK_SCROLL
)
98 ConioUnpause(Console
, PAUSED_FROM_KEYBOARD
);
99 return STATUS_SUCCESS
;
104 /* Add event to the queue */
105 ConInRec
= ConsoleAllocHeap(0, sizeof(ConsoleInput
));
106 if (ConInRec
== NULL
) return STATUS_INSUFFICIENT_RESOURCES
;
108 ConInRec
->InputEvent
= *InputEvent
;
112 /* Append the event to the end of the queue */
113 InsertTailList(&Console
->InputBuffer
.InputEvents
, &ConInRec
->ListEntry
);
117 /* Append the event to the beginning of the queue */
118 InsertHeadList(&Console
->InputBuffer
.InputEvents
, &ConInRec
->ListEntry
);
121 SetEvent(Console
->InputBuffer
.ActiveEvent
);
122 CsrNotifyWait(&Console
->InputBuffer
.ReadWaitQueue
,
126 if (!IsListEmpty(&Console
->InputBuffer
.ReadWaitQueue
))
128 CsrDereferenceWait(&Console
->InputBuffer
.ReadWaitQueue
);
131 return STATUS_SUCCESS
;
135 ConioProcessInputEvent(PCONSOLE Console
,
136 PINPUT_RECORD InputEvent
)
138 return ConioAddInputEvent(Console
, InputEvent
, TRUE
);
142 PurgeInputBuffer(PCONSOLE Console
)
144 PLIST_ENTRY CurrentEntry
;
147 while (!IsListEmpty(&Console
->InputBuffer
.InputEvents
))
149 CurrentEntry
= RemoveHeadList(&Console
->InputBuffer
.InputEvents
);
150 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
151 ConsoleFreeHeap(Event
);
154 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
158 * This function explicitely references Console->ActiveBuffer
159 * (and also makes use of keyboard functions...).
160 * It is possible that it will move into frontends...
163 ConDrvProcessKey(IN PCONSOLE Console
,
165 IN UINT VirtualKeyCode
,
166 IN UINT VirtualScanCode
,
167 IN WCHAR UnicodeChar
,
169 IN BYTE KeyStateCtrl
)
173 /* process Ctrl-C and Ctrl-Break */
174 if ( Console
->InputBuffer
.Mode
& ENABLE_PROCESSED_INPUT
&&
175 Down
&& (VirtualKeyCode
== VK_PAUSE
|| VirtualKeyCode
== 'C') &&
176 (ShiftState
& (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
) || KeyStateCtrl
& 0x80) )
178 DPRINT1("Console_Api Ctrl-C\n");
179 ConDrvConsoleProcessCtrlEvent(Console
, 0, CTRL_C_EVENT
);
181 if (Console
->LineBuffer
&& !Console
->LineComplete
)
183 /* Line input is in progress; end it */
184 Console
->LinePos
= Console
->LineSize
= 0;
185 Console
->LineComplete
= TRUE
;
190 if ( (ShiftState
& (RIGHT_ALT_PRESSED
| LEFT_ALT_PRESSED
)) != 0 &&
191 (VK_UP
== VirtualKeyCode
|| VK_DOWN
== VirtualKeyCode
) )
195 /* scroll up or down */
196 if (VK_UP
== VirtualKeyCode
)
198 /* only scroll up if there is room to scroll up into */
199 if (Console
->ActiveBuffer
->CursorPosition
.Y
!= Console
->ActiveBuffer
->ScreenBufferSize
.Y
- 1)
201 Console
->ActiveBuffer
->VirtualY
= (Console
->ActiveBuffer
->VirtualY
+
202 Console
->ActiveBuffer
->ScreenBufferSize
.Y
- 1) %
203 Console
->ActiveBuffer
->ScreenBufferSize
.Y
;
204 Console
->ActiveBuffer
->CursorPosition
.Y
++;
209 /* only scroll down if there is room to scroll down into */
210 if (Console
->ActiveBuffer
->CursorPosition
.Y
!= 0)
212 Console
->ActiveBuffer
->VirtualY
= (Console
->ActiveBuffer
->VirtualY
+ 1) %
213 Console
->ActiveBuffer
->ScreenBufferSize
.Y
;
214 Console
->ActiveBuffer
->CursorPosition
.Y
--;
218 ConioDrawConsole(Console
);
222 er
.EventType
= KEY_EVENT
;
223 er
.Event
.KeyEvent
.bKeyDown
= Down
;
224 er
.Event
.KeyEvent
.wRepeatCount
= 1;
225 er
.Event
.KeyEvent
.wVirtualKeyCode
= VirtualKeyCode
;
226 er
.Event
.KeyEvent
.wVirtualScanCode
= VirtualScanCode
;
227 er
.Event
.KeyEvent
.uChar
.UnicodeChar
= UnicodeChar
;
228 er
.Event
.KeyEvent
.dwControlKeyState
= ShiftState
;
230 ConioProcessInputEvent(Console
, &er
);
234 /* PUBLIC DRIVER APIS *********************************************************/
237 ConDrvReadConsole(IN PCONSOLE Console
,
238 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
241 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl
,
242 IN ULONG NumCharsToRead
,
243 OUT PULONG NumCharsRead OPTIONAL
)
245 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
246 NTSTATUS Status
= STATUS_PENDING
;
247 PLIST_ENTRY CurrentEntry
;
251 if (Console
== NULL
|| InputBuffer
== NULL
|| /* Buffer == NULL || */
252 ReadControl
== NULL
|| ReadControl
->nLength
!= sizeof(CONSOLE_READCONSOLE_CONTROL
))
254 return STATUS_INVALID_PARAMETER
;
257 /* Validity checks */
258 ASSERT(Console
== InputBuffer
->Header
.Console
);
259 ASSERT( (Buffer
!= NULL
&& NumCharsToRead
>= 0) ||
260 (Buffer
== NULL
&& NumCharsToRead
== 0) );
262 /* We haven't read anything (yet) */
264 i
= ReadControl
->nInitialChars
;
266 if (InputBuffer
->Mode
& ENABLE_LINE_INPUT
)
268 if (Console
->LineBuffer
== NULL
)
270 /* Starting a new line */
271 Console
->LineMaxSize
= (WORD
)max(256, NumCharsToRead
);
273 Console
->LineBuffer
= ConsoleAllocHeap(0, Console
->LineMaxSize
* sizeof(WCHAR
));
274 if (Console
->LineBuffer
== NULL
) return STATUS_NO_MEMORY
;
276 Console
->LineComplete
= FALSE
;
277 Console
->LineUpPressed
= FALSE
;
278 Console
->LineInsertToggle
= Console
->InsertMode
;
279 Console
->LineWakeupMask
= ReadControl
->dwCtrlWakeupMask
;
280 Console
->LineSize
= ReadControl
->nInitialChars
;
281 Console
->LinePos
= Console
->LineSize
;
284 * Pre-filling the buffer is only allowed in the Unicode API,
285 * so we don't need to worry about ANSI <-> Unicode conversion.
287 memcpy(Console
->LineBuffer
, Buffer
, Console
->LineSize
* sizeof(WCHAR
));
288 if (Console
->LineSize
== Console
->LineMaxSize
)
290 Console
->LineComplete
= TRUE
;
291 Console
->LinePos
= 0;
295 /* If we don't have a complete line yet, process the pending input */
296 while (!Console
->LineComplete
&& !IsListEmpty(&InputBuffer
->InputEvents
))
298 /* Remove input event from queue */
299 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
300 if (IsListEmpty(&InputBuffer
->InputEvents
))
302 ResetEvent(InputBuffer
->ActiveEvent
);
304 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
306 /* Only pay attention to key down */
307 if (Input
->InputEvent
.EventType
== KEY_EVENT
&&
308 Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
)
310 LineInputKeyDown(Console
, &Input
->InputEvent
.Event
.KeyEvent
);
311 ReadControl
->dwControlKeyState
= Input
->InputEvent
.Event
.KeyEvent
.dwControlKeyState
;
313 ConsoleFreeHeap(Input
);
316 /* Check if we have a complete line to read from */
317 if (Console
->LineComplete
)
319 while (i
< NumCharsToRead
&& Console
->LinePos
!= Console
->LineSize
)
321 WCHAR Char
= Console
->LineBuffer
[Console
->LinePos
++];
325 ((PWCHAR
)Buffer
)[i
] = Char
;
329 ConsoleInputUnicodeCharToAnsiChar(Console
, &((PCHAR
)Buffer
)[i
], &Char
);
334 if (Console
->LinePos
== Console
->LineSize
)
336 /* Entire line has been read */
337 ConsoleFreeHeap(Console
->LineBuffer
);
338 Console
->LineBuffer
= NULL
;
341 Status
= STATUS_SUCCESS
;
346 /* Character input */
347 while (i
< NumCharsToRead
&& !IsListEmpty(&InputBuffer
->InputEvents
))
349 /* Remove input event from queue */
350 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
351 if (IsListEmpty(&InputBuffer
->InputEvents
))
353 ResetEvent(InputBuffer
->ActiveEvent
);
355 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
357 /* Only pay attention to valid ASCII chars, on key down */
358 if (Input
->InputEvent
.EventType
== KEY_EVENT
&&
359 Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
&&
360 Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
!= L
'\0')
362 WCHAR Char
= Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
;
366 ((PWCHAR
)Buffer
)[i
] = Char
;
370 ConsoleInputUnicodeCharToAnsiChar(Console
, &((PCHAR
)Buffer
)[i
], &Char
);
374 /* Did read something */
375 Status
= STATUS_SUCCESS
;
377 ConsoleFreeHeap(Input
);
381 if (NumCharsRead
) *NumCharsRead
= i
;
387 ConDrvGetConsoleInput(IN PCONSOLE Console
,
388 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
389 IN BOOLEAN KeepEvents
,
390 IN BOOLEAN WaitForMoreEvents
,
392 OUT PINPUT_RECORD InputRecord
,
393 IN ULONG NumEventsToRead
,
394 OUT PULONG NumEventsRead OPTIONAL
)
396 PLIST_ENTRY CurrentInput
;
400 if (Console
== NULL
|| InputBuffer
== NULL
/* || InputRecord == NULL */)
401 return STATUS_INVALID_PARAMETER
;
403 /* Validity checks */
404 ASSERT(Console
== InputBuffer
->Header
.Console
);
405 ASSERT( (InputRecord
!= NULL
&& NumEventsToRead
>= 0) ||
406 (InputRecord
== NULL
&& NumEventsToRead
== 0) );
408 // Do NOT do that !! Use the existing number of events already read, if any...
409 // if (NumEventsRead) *NumEventsRead = 0;
411 if (IsListEmpty(&InputBuffer
->InputEvents
))
414 * No input is available. Wait for more input if requested,
415 * otherwise, we don't wait, so we return success.
417 return (WaitForMoreEvents
? STATUS_PENDING
: STATUS_SUCCESS
);
420 /* Only get input if there is any */
421 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
422 if (NumEventsRead
) i
= *NumEventsRead
; // We will read the remaining events...
424 while ((CurrentInput
!= &InputBuffer
->InputEvents
) && (i
< NumEventsToRead
))
426 Input
= CONTAINING_RECORD(CurrentInput
, ConsoleInput
, ListEntry
);
428 *InputRecord
= Input
->InputEvent
;
432 ConioInputEventToAnsi(InputBuffer
->Header
.Console
, InputRecord
);
437 CurrentInput
= CurrentInput
->Flink
;
439 /* Remove the events from the queue if needed */
442 RemoveEntryList(&Input
->ListEntry
);
443 ConsoleFreeHeap(Input
);
447 if (NumEventsRead
) *NumEventsRead
= i
;
449 if (IsListEmpty(&InputBuffer
->InputEvents
))
451 ResetEvent(InputBuffer
->ActiveEvent
);
454 /* We read all the inputs available, we return success */
455 return STATUS_SUCCESS
;
459 ConDrvWriteConsoleInput(IN PCONSOLE Console
,
460 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
462 IN BOOLEAN AppendToEnd
,
463 IN PINPUT_RECORD InputRecord
,
464 IN ULONG NumEventsToWrite
,
465 OUT PULONG NumEventsWritten OPTIONAL
)
467 NTSTATUS Status
= STATUS_SUCCESS
;
470 if (Console
== NULL
|| InputBuffer
== NULL
/* || InputRecord == NULL */)
471 return STATUS_INVALID_PARAMETER
;
473 /* Validity checks */
474 ASSERT(Console
== InputBuffer
->Header
.Console
);
475 ASSERT( (InputRecord
!= NULL
&& NumEventsToWrite
>= 0) ||
476 (InputRecord
== NULL
&& NumEventsToWrite
== 0) );
478 // Do NOT do that !! Use the existing number of events already written, if any...
479 // if (NumEventsWritten) *NumEventsWritten = 0;
481 for (i
= (NumEventsWritten
? *NumEventsWritten
: 0); i
< NumEventsToWrite
&& NT_SUCCESS(Status
); ++i
)
485 ConioInputEventToUnicode(Console
, InputRecord
);
488 Status
= ConioAddInputEvent(Console
, InputRecord
++, AppendToEnd
);
491 if (NumEventsWritten
) *NumEventsWritten
= i
;
497 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console
,
498 IN PCONSOLE_INPUT_BUFFER InputBuffer
)
500 PLIST_ENTRY CurrentEntry
;
503 if (Console
== NULL
|| InputBuffer
== NULL
)
504 return STATUS_INVALID_PARAMETER
;
507 ASSERT(Console
== InputBuffer
->Header
.Console
);
509 /* Discard all entries in the input event queue */
510 while (!IsListEmpty(&InputBuffer
->InputEvents
))
512 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
513 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
514 ConsoleFreeHeap(Event
);
516 ResetEvent(InputBuffer
->ActiveEvent
);
518 return STATUS_SUCCESS
;
522 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console
,
523 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
524 OUT PULONG NumberOfEvents
)
526 PLIST_ENTRY CurrentInput
;
528 if (Console
== NULL
|| InputBuffer
== NULL
|| NumberOfEvents
== NULL
)
529 return STATUS_INVALID_PARAMETER
;
532 ASSERT(Console
== InputBuffer
->Header
.Console
);
536 /* If there are any events ... */
537 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
538 while (CurrentInput
!= &InputBuffer
->InputEvents
)
540 CurrentInput
= CurrentInput
->Flink
;
544 return STATUS_SUCCESS
;