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 *******************************************************************/
13 #include "include/conio.h"
14 #include "include/conio2.h"
16 #include "lineinput.h"
22 /* GLOBALS ********************************************************************/
24 #define ConSrvGetInputBuffer(ProcessData, Handle, Ptr, Access, LockConsole) \
25 ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), NULL, \
26 (Access), (LockConsole), INPUT_BUFFER)
27 #define ConSrvGetInputBufferAndHandleEntry(ProcessData, Handle, Ptr, Entry, Access, LockConsole) \
28 ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), (Entry), \
29 (Access), (LockConsole), INPUT_BUFFER)
30 #define ConSrvReleaseInputBuffer(Buff, IsConsoleLocked) \
31 ConSrvReleaseObject(&(Buff)->Header, (IsConsoleLocked))
34 #define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
35 WideCharToMultiByte((Console)->CodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
37 #define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
38 MultiByteToWideChar((Console)->CodePage, 0, (sChar), 1, (dWChar), 1)
40 typedef struct ConsoleInput_t
43 INPUT_RECORD InputEvent
;
47 /* PRIVATE FUNCTIONS **********************************************************/
50 ConioInputEventToAnsi(PCONSOLE Console
, PINPUT_RECORD InputEvent
)
52 if (InputEvent
->EventType
== KEY_EVENT
)
54 WCHAR UnicodeChar
= InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
;
55 InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
= 0;
56 ConsoleInputUnicodeCharToAnsiChar(Console
,
57 &InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
,
63 ConioProcessInputEvent(PCONSOLE Console
,
64 PINPUT_RECORD InputEvent
)
66 ConsoleInput
*ConInRec
;
68 /* Check for pause or unpause */
69 if (InputEvent
->EventType
== KEY_EVENT
&& InputEvent
->Event
.KeyEvent
.bKeyDown
)
71 WORD vk
= InputEvent
->Event
.KeyEvent
.wVirtualKeyCode
;
72 if (!(Console
->PauseFlags
& PAUSED_FROM_KEYBOARD
))
74 DWORD cks
= InputEvent
->Event
.KeyEvent
.dwControlKeyState
;
75 if (Console
->InputBuffer
.Mode
& ENABLE_LINE_INPUT
&&
76 (vk
== VK_PAUSE
|| (vk
== 'S' &&
77 (cks
& (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
)) &&
78 !(cks
& (LEFT_ALT_PRESSED
| RIGHT_ALT_PRESSED
)))))
80 ConioPause(Console
, PAUSED_FROM_KEYBOARD
);
81 return STATUS_SUCCESS
;
86 if ((vk
< VK_SHIFT
|| vk
> VK_CAPITAL
) && vk
!= VK_LWIN
&&
87 vk
!= VK_RWIN
&& vk
!= VK_NUMLOCK
&& vk
!= VK_SCROLL
)
89 ConioUnpause(Console
, PAUSED_FROM_KEYBOARD
);
90 return STATUS_SUCCESS
;
95 /* Add event to the queue */
96 ConInRec
= ConsoleAllocHeap(0, sizeof(ConsoleInput
));
97 if (ConInRec
== NULL
) return STATUS_INSUFFICIENT_RESOURCES
;
99 ConInRec
->InputEvent
= *InputEvent
;
100 InsertTailList(&Console
->InputBuffer
.InputEvents
, &ConInRec
->ListEntry
);
102 SetEvent(Console
->InputBuffer
.ActiveEvent
);
103 CsrNotifyWait(&Console
->InputBuffer
.ReadWaitQueue
,
107 if (!IsListEmpty(&Console
->InputBuffer
.ReadWaitQueue
))
109 CsrDereferenceWait(&Console
->InputBuffer
.ReadWaitQueue
);
112 return STATUS_SUCCESS
;
116 PurgeInputBuffer(PCONSOLE Console
)
118 PLIST_ENTRY CurrentEntry
;
121 while (!IsListEmpty(&Console
->InputBuffer
.InputEvents
))
123 CurrentEntry
= RemoveHeadList(&Console
->InputBuffer
.InputEvents
);
124 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
125 ConsoleFreeHeap(Event
);
128 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
132 ConDrvProcessKey(IN PCONSOLE Console
,
134 IN UINT VirtualKeyCode
,
135 IN UINT VirtualScanCode
,
136 IN WCHAR UnicodeChar
,
138 IN BYTE KeyStateCtrl
)
142 /* process Ctrl-C and Ctrl-Break */
143 if ( Console
->InputBuffer
.Mode
& ENABLE_PROCESSED_INPUT
&&
144 Down
&& (VirtualKeyCode
== VK_PAUSE
|| VirtualKeyCode
== 'C') &&
145 (ShiftState
& (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
) || KeyStateCtrl
& 0x80) )
147 DPRINT1("Console_Api Ctrl-C\n");
148 ConDrvConsoleProcessCtrlEvent(Console
, 0, CTRL_C_EVENT
);
150 if (Console
->LineBuffer
&& !Console
->LineComplete
)
152 /* Line input is in progress; end it */
153 Console
->LinePos
= Console
->LineSize
= 0;
154 Console
->LineComplete
= TRUE
;
159 if ( (ShiftState
& (RIGHT_ALT_PRESSED
| LEFT_ALT_PRESSED
)) != 0 &&
160 (VK_UP
== VirtualKeyCode
|| VK_DOWN
== VirtualKeyCode
) )
164 /* scroll up or down */
165 if (VK_UP
== VirtualKeyCode
)
167 /* only scroll up if there is room to scroll up into */
168 if (Console
->ActiveBuffer
->CursorPosition
.Y
!= Console
->ActiveBuffer
->ScreenBufferSize
.Y
- 1)
170 Console
->ActiveBuffer
->VirtualY
= (Console
->ActiveBuffer
->VirtualY
+
171 Console
->ActiveBuffer
->ScreenBufferSize
.Y
- 1) %
172 Console
->ActiveBuffer
->ScreenBufferSize
.Y
;
173 Console
->ActiveBuffer
->CursorPosition
.Y
++;
178 /* only scroll down if there is room to scroll down into */
179 if (Console
->ActiveBuffer
->CursorPosition
.Y
!= 0)
181 Console
->ActiveBuffer
->VirtualY
= (Console
->ActiveBuffer
->VirtualY
+ 1) %
182 Console
->ActiveBuffer
->ScreenBufferSize
.Y
;
183 Console
->ActiveBuffer
->CursorPosition
.Y
--;
187 ConioDrawConsole(Console
);
191 er
.EventType
= KEY_EVENT
;
192 er
.Event
.KeyEvent
.bKeyDown
= Down
;
193 er
.Event
.KeyEvent
.wRepeatCount
= 1;
194 er
.Event
.KeyEvent
.wVirtualKeyCode
= VirtualKeyCode
;
195 er
.Event
.KeyEvent
.wVirtualScanCode
= VirtualScanCode
;
196 er
.Event
.KeyEvent
.uChar
.UnicodeChar
= UnicodeChar
;
197 er
.Event
.KeyEvent
.dwControlKeyState
= ShiftState
;
199 ConioProcessInputEvent(Console
, &er
);
203 /* PUBLIC DRIVER APIS *********************************************************/
206 ConDrvReadConsole(IN PCONSOLE Console
,
207 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
210 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl
,
211 IN ULONG NumCharsToRead
,
212 OUT PULONG NumCharsRead OPTIONAL
)
214 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
215 NTSTATUS Status
= STATUS_PENDING
;
216 PLIST_ENTRY CurrentEntry
;
218 ULONG i
= ReadControl
->nInitialChars
;
220 if (Console
== NULL
|| InputBuffer
== NULL
|| /* Buffer == NULL || */
221 ReadControl
== NULL
|| ReadControl
->nLength
!= sizeof(CONSOLE_READCONSOLE_CONTROL
))
223 return STATUS_INVALID_PARAMETER
;
226 /* Validity checks */
227 ASSERT(Console
== InputBuffer
->Header
.Console
);
228 ASSERT( (Buffer
!= NULL
&& NumCharsToRead
>= 0) ||
229 (Buffer
== NULL
&& NumCharsToRead
== 0) );
231 /* We haven't read anything (yet) */
233 if (InputBuffer
->Mode
& ENABLE_LINE_INPUT
)
235 if (Console
->LineBuffer
== NULL
)
237 /* Starting a new line */
238 Console
->LineMaxSize
= (WORD
)max(256, NumCharsToRead
);
240 Console
->LineBuffer
= ConsoleAllocHeap(0, Console
->LineMaxSize
* sizeof(WCHAR
));
241 if (Console
->LineBuffer
== NULL
) return STATUS_NO_MEMORY
;
243 Console
->LineComplete
= FALSE
;
244 Console
->LineUpPressed
= FALSE
;
245 Console
->LineInsertToggle
= 0;
246 Console
->LineWakeupMask
= ReadControl
->dwCtrlWakeupMask
;
247 Console
->LineSize
= ReadControl
->nInitialChars
;
248 Console
->LinePos
= Console
->LineSize
;
251 * Pre-filling the buffer is only allowed in the Unicode API,
252 * so we don't need to worry about ANSI <-> Unicode conversion.
254 memcpy(Console
->LineBuffer
, Buffer
, Console
->LineSize
* sizeof(WCHAR
));
255 if (Console
->LineSize
== Console
->LineMaxSize
)
257 Console
->LineComplete
= TRUE
;
258 Console
->LinePos
= 0;
262 /* If we don't have a complete line yet, process the pending input */
263 while (!Console
->LineComplete
&& !IsListEmpty(&InputBuffer
->InputEvents
))
265 /* Remove input event from queue */
266 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
267 if (IsListEmpty(&InputBuffer
->InputEvents
))
269 ResetEvent(InputBuffer
->ActiveEvent
);
271 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
273 /* Only pay attention to key down */
274 if (Input
->InputEvent
.EventType
== KEY_EVENT
&&
275 Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
)
277 LineInputKeyDown(Console
, &Input
->InputEvent
.Event
.KeyEvent
);
278 ReadControl
->dwControlKeyState
= Input
->InputEvent
.Event
.KeyEvent
.dwControlKeyState
;
280 ConsoleFreeHeap(Input
);
283 /* Check if we have a complete line to read from */
284 if (Console
->LineComplete
)
286 while (i
< NumCharsToRead
&& Console
->LinePos
!= Console
->LineSize
)
288 WCHAR Char
= Console
->LineBuffer
[Console
->LinePos
++];
292 ((PWCHAR
)Buffer
)[i
] = Char
;
296 ConsoleInputUnicodeCharToAnsiChar(Console
, &((PCHAR
)Buffer
)[i
], &Char
);
301 if (Console
->LinePos
== Console
->LineSize
)
303 /* Entire line has been read */
304 ConsoleFreeHeap(Console
->LineBuffer
);
305 Console
->LineBuffer
= NULL
;
308 Status
= STATUS_SUCCESS
;
313 /* Character input */
314 while (i
< NumCharsToRead
&& !IsListEmpty(&InputBuffer
->InputEvents
))
316 /* Remove input event from queue */
317 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
318 if (IsListEmpty(&InputBuffer
->InputEvents
))
320 ResetEvent(InputBuffer
->ActiveEvent
);
322 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
324 /* Only pay attention to valid ASCII chars, on key down */
325 if (Input
->InputEvent
.EventType
== KEY_EVENT
&&
326 Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
&&
327 Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
!= L
'\0')
329 WCHAR Char
= Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
;
333 ((PWCHAR
)Buffer
)[i
] = Char
;
337 ConsoleInputUnicodeCharToAnsiChar(Console
, &((PCHAR
)Buffer
)[i
], &Char
);
341 /* Did read something */
342 Status
= STATUS_SUCCESS
;
344 ConsoleFreeHeap(Input
);
348 if (NumCharsRead
) *NumCharsRead
= i
;
354 ConDrvGetConsoleInput(IN PCONSOLE Console
,
355 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
356 IN BOOLEAN WaitForMoreEvents
,
358 OUT PINPUT_RECORD InputRecord
,
359 IN ULONG NumEventsToRead
,
360 OUT PULONG NumEventsRead OPTIONAL
)
362 PLIST_ENTRY CurrentInput
;
366 if (Console
== NULL
|| InputBuffer
== NULL
/* || InputRecord == NULL */)
367 return STATUS_INVALID_PARAMETER
;
369 /* Validity checks */
370 ASSERT(Console
== InputBuffer
->Header
.Console
);
371 ASSERT( (InputRecord
!= NULL
&& NumEventsToRead
>= 0) ||
372 (InputRecord
== NULL
&& NumEventsToRead
== 0) );
374 // Do NOT do that !! Use the existing number of events already read, if any...
375 // if (NumEventsRead) *NumEventsRead = 0;
377 if (IsListEmpty(&InputBuffer
->InputEvents
))
380 * No input is available. Wait for more input if requested,
381 * otherwise, we don't wait, so we return success.
383 return (WaitForMoreEvents
? STATUS_PENDING
: STATUS_SUCCESS
);
386 /* Only get input if there is any */
387 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
388 if (NumEventsRead
) i
= *NumEventsRead
; // We will read the remaining events...
390 while ((CurrentInput
!= &InputBuffer
->InputEvents
) && (i
< NumEventsToRead
))
392 Input
= CONTAINING_RECORD(CurrentInput
, ConsoleInput
, ListEntry
);
394 *InputRecord
= Input
->InputEvent
;
398 ConioInputEventToAnsi(InputBuffer
->Header
.Console
, InputRecord
);
403 CurrentInput
= CurrentInput
->Flink
;
405 if (WaitForMoreEvents
) // TRUE --> Read, we remove inputs from the buffer ; FALSE --> Peek, we keep inputs.
407 RemoveEntryList(&Input
->ListEntry
);
408 ConsoleFreeHeap(Input
);
412 if (NumEventsRead
) *NumEventsRead
= i
;
414 if (IsListEmpty(&InputBuffer
->InputEvents
))
416 ResetEvent(InputBuffer
->ActiveEvent
);
419 /* We read all the inputs available, we return success */
420 return STATUS_SUCCESS
;
424 ConDrvWriteConsoleInput(IN PCONSOLE Console
,
425 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
427 IN PINPUT_RECORD InputRecord
,
428 IN ULONG NumEventsToWrite
,
429 OUT PULONG NumEventsWritten OPTIONAL
)
431 NTSTATUS Status
= STATUS_SUCCESS
;
434 if (Console
== NULL
|| InputBuffer
== NULL
/* || InputRecord == NULL */)
435 return STATUS_INVALID_PARAMETER
;
437 /* Validity checks */
438 ASSERT(Console
== InputBuffer
->Header
.Console
);
439 ASSERT( (InputRecord
!= NULL
&& NumEventsToWrite
>= 0) ||
440 (InputRecord
== NULL
&& NumEventsToWrite
== 0) );
442 // Do NOT do that !! Use the existing number of events already written, if any...
443 // if (NumEventsWritten) *NumEventsWritten = 0;
445 for (i
= (NumEventsWritten
? *NumEventsWritten
: 0); i
< NumEventsToWrite
&& NT_SUCCESS(Status
); ++i
)
447 if (InputRecord
->EventType
== KEY_EVENT
&& !Unicode
)
449 CHAR AsciiChar
= InputRecord
->Event
.KeyEvent
.uChar
.AsciiChar
;
450 ConsoleInputAnsiCharToUnicodeChar(Console
,
451 &InputRecord
->Event
.KeyEvent
.uChar
.UnicodeChar
,
455 Status
= ConioProcessInputEvent(Console
, InputRecord
++);
458 if (NumEventsWritten
) *NumEventsWritten
= i
;
464 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console
,
465 IN PCONSOLE_INPUT_BUFFER InputBuffer
)
467 PLIST_ENTRY CurrentEntry
;
470 if (Console
== NULL
|| InputBuffer
== NULL
)
471 return STATUS_INVALID_PARAMETER
;
474 ASSERT(Console
== InputBuffer
->Header
.Console
);
476 /* Discard all entries in the input event queue */
477 while (!IsListEmpty(&InputBuffer
->InputEvents
))
479 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
480 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
481 ConsoleFreeHeap(Event
);
483 ResetEvent(InputBuffer
->ActiveEvent
);
485 return STATUS_SUCCESS
;
489 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console
,
490 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
491 OUT PULONG NumEvents
)
493 PLIST_ENTRY CurrentInput
;
495 if (Console
== NULL
|| InputBuffer
== NULL
|| NumEvents
== NULL
)
496 return STATUS_INVALID_PARAMETER
;
499 ASSERT(Console
== InputBuffer
->Header
.Console
);
503 /* If there are any events ... */
504 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
505 while (CurrentInput
!= &InputBuffer
->InputEvents
)
507 CurrentInput
= CurrentInput
->Flink
;
511 return STATUS_SUCCESS
;