2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/consrv/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"
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
;
46 typedef struct _GET_INPUT_INFO
48 PCSR_THREAD CallingThread
; // The thread which called the input API.
49 PVOID HandleEntry
; // The handle data associated with the wait thread.
50 PCONSOLE_INPUT_BUFFER InputBuffer
; // The input buffer corresponding to the handle.
51 } GET_INPUT_INFO
, *PGET_INPUT_INFO
;
54 /* PRIVATE FUNCTIONS **********************************************************/
57 ConioInputEventToAnsi(PCONSOLE Console
, PINPUT_RECORD InputEvent
)
59 if (InputEvent
->EventType
== KEY_EVENT
)
61 WCHAR UnicodeChar
= InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
;
62 InputEvent
->Event
.KeyEvent
.uChar
.UnicodeChar
= 0;
63 ConsoleInputUnicodeCharToAnsiChar(Console
,
64 &InputEvent
->Event
.KeyEvent
.uChar
.AsciiChar
,
70 ConioProcessInputEvent(PCONSOLE Console
,
71 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
= RtlAllocateHeap(ConSrvHeap
, 0, sizeof(ConsoleInput
));
104 if (ConInRec
== NULL
)
105 return STATUS_INSUFFICIENT_RESOURCES
;
106 ConInRec
->InputEvent
= *InputEvent
;
107 InsertTailList(&Console
->InputBuffer
.InputEvents
, &ConInRec
->ListEntry
);
109 SetEvent(Console
->InputBuffer
.ActiveEvent
);
110 CsrNotifyWait(&Console
->InputBuffer
.ReadWaitQueue
,
114 if (!IsListEmpty(&Console
->InputBuffer
.ReadWaitQueue
))
116 CsrDereferenceWait(&Console
->InputBuffer
.ReadWaitQueue
);
119 return STATUS_SUCCESS
;
123 PurgeInputBuffer(PCONSOLE Console
)
125 PLIST_ENTRY CurrentEntry
;
128 while (!IsListEmpty(&Console
->InputBuffer
.InputEvents
))
130 CurrentEntry
= RemoveHeadList(&Console
->InputBuffer
.InputEvents
);
131 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
132 RtlFreeHeap(ConSrvHeap
, 0, Event
);
135 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
138 static DWORD FASTCALL
139 ConioGetShiftState(PBYTE KeyState
, LPARAM lParam
)
143 if (KeyState
[VK_CAPITAL
] & 0x01)
144 ssOut
|= CAPSLOCK_ON
;
146 if (KeyState
[VK_NUMLOCK
] & 0x01)
149 if (KeyState
[VK_SCROLL
] & 0x01)
150 ssOut
|= SCROLLLOCK_ON
;
152 if (KeyState
[VK_SHIFT
] & 0x80)
153 ssOut
|= SHIFT_PRESSED
;
155 if (KeyState
[VK_LCONTROL
] & 0x80)
156 ssOut
|= LEFT_CTRL_PRESSED
;
157 if (KeyState
[VK_RCONTROL
] & 0x80)
158 ssOut
|= RIGHT_CTRL_PRESSED
;
160 if (KeyState
[VK_LMENU
] & 0x80)
161 ssOut
|= LEFT_ALT_PRESSED
;
162 if (KeyState
[VK_RMENU
] & 0x80)
163 ssOut
|= RIGHT_ALT_PRESSED
;
165 /* See WM_CHAR MSDN documentation for instance */
166 if (lParam
& 0x01000000)
167 ssOut
|= ENHANCED_KEY
;
173 ConioProcessKey(PCONSOLE Console
, MSG
* msg
)
175 static BYTE KeyState
[256] = { 0 };
176 /* MSDN mentions that you should use the last virtual key code received
177 * when putting a virtual key identity to a WM_CHAR message since multiple
178 * or translated keys may be involved. */
179 static UINT LastVirtualKey
= 0;
184 UINT VirtualScanCode
;
187 BOOLEAN Fake
; // synthesized, not a real event
188 BOOLEAN NotChar
; // message should not be used to return a character
192 DPRINT1("No Active Console!\n");
197 VirtualScanCode
= (msg
->lParam
>> 16) & 0xff;
198 Down
= msg
->message
== WM_KEYDOWN
|| msg
->message
== WM_CHAR
||
199 msg
->message
== WM_SYSKEYDOWN
|| msg
->message
== WM_SYSCHAR
;
201 GetKeyboardState(KeyState
);
202 ShiftState
= ConioGetShiftState(KeyState
, msg
->lParam
);
204 if (msg
->message
== WM_CHAR
|| msg
->message
== WM_SYSCHAR
)
206 VirtualKeyCode
= LastVirtualKey
;
207 UnicodeChar
= msg
->wParam
;
214 VirtualKeyCode
= msg
->wParam
;
215 RetChars
= ToUnicodeEx(VirtualKeyCode
,
222 UnicodeChar
= (1 == RetChars
? Chars
[0] : 0);
225 er
.EventType
= KEY_EVENT
;
226 er
.Event
.KeyEvent
.bKeyDown
= Down
;
227 er
.Event
.KeyEvent
.wRepeatCount
= RepeatCount
;
228 er
.Event
.KeyEvent
.uChar
.UnicodeChar
= UnicodeChar
;
229 er
.Event
.KeyEvent
.dwControlKeyState
= ShiftState
;
230 er
.Event
.KeyEvent
.wVirtualKeyCode
= VirtualKeyCode
;
231 er
.Event
.KeyEvent
.wVirtualScanCode
= VirtualScanCode
;
233 if (ConioProcessKeyCallback(Console
,
243 Fake
= UnicodeChar
&&
244 (msg
->message
!= WM_CHAR
&& msg
->message
!= WM_SYSCHAR
&&
245 msg
->message
!= WM_KEYUP
&& msg
->message
!= WM_SYSKEYUP
);
246 NotChar
= (msg
->message
!= WM_CHAR
&& msg
->message
!= WM_SYSCHAR
);
247 if (NotChar
) LastVirtualKey
= msg
->wParam
;
249 DPRINT("CONSRV: %s %s %s %s %02x %02x '%lc' %04x\n",
250 Down
? "down" : "up ",
251 (msg
->message
== WM_CHAR
|| msg
->message
== WM_SYSCHAR
) ?
253 Fake
? "fake" : "real",
254 NotChar
? "notc" : "char",
257 (UnicodeChar
>= L
' ') ? UnicodeChar
: L
'.',
262 /* process Ctrl-C and Ctrl-Break */
263 if (Console
->InputBuffer
.Mode
& ENABLE_PROCESSED_INPUT
&&
264 er
.Event
.KeyEvent
.bKeyDown
&&
265 ((er
.Event
.KeyEvent
.wVirtualKeyCode
== VK_PAUSE
) ||
266 (er
.Event
.KeyEvent
.wVirtualKeyCode
== 'C')) &&
267 (er
.Event
.KeyEvent
.dwControlKeyState
& (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
) || KeyState
[VK_CONTROL
] & 0x80))
269 DPRINT1("Console_Api Ctrl-C\n");
270 ConSrvConsoleProcessCtrlEvent(Console
, 0, CTRL_C_EVENT
);
272 if (Console
->LineBuffer
&& !Console
->LineComplete
)
274 /* Line input is in progress; end it */
275 Console
->LinePos
= Console
->LineSize
= 0;
276 Console
->LineComplete
= TRUE
;
281 if (0 != (er
.Event
.KeyEvent
.dwControlKeyState
282 & (RIGHT_ALT_PRESSED
| LEFT_ALT_PRESSED
))
283 && (VK_UP
== er
.Event
.KeyEvent
.wVirtualKeyCode
284 || VK_DOWN
== er
.Event
.KeyEvent
.wVirtualKeyCode
))
286 if (er
.Event
.KeyEvent
.bKeyDown
)
288 /* scroll up or down */
289 if (VK_UP
== er
.Event
.KeyEvent
.wVirtualKeyCode
)
291 /* only scroll up if there is room to scroll up into */
292 if (Console
->ActiveBuffer
->CursorPosition
.Y
!= Console
->ActiveBuffer
->ScreenBufferSize
.Y
- 1)
294 Console
->ActiveBuffer
->VirtualY
= (Console
->ActiveBuffer
->VirtualY
+
295 Console
->ActiveBuffer
->ScreenBufferSize
.Y
- 1) %
296 Console
->ActiveBuffer
->ScreenBufferSize
.Y
;
297 Console
->ActiveBuffer
->CursorPosition
.Y
++;
302 /* only scroll down if there is room to scroll down into */
303 if (Console
->ActiveBuffer
->CursorPosition
.Y
!= 0)
305 Console
->ActiveBuffer
->VirtualY
= (Console
->ActiveBuffer
->VirtualY
+ 1) %
306 Console
->ActiveBuffer
->ScreenBufferSize
.Y
;
307 Console
->ActiveBuffer
->CursorPosition
.Y
--;
310 ConioDrawConsole(Console
);
314 ConioProcessInputEvent(Console
, &er
);
318 WaitBeforeReading(IN PGET_INPUT_INFO InputInfo
,
319 IN PCSR_API_MESSAGE ApiMessage
,
320 IN CSR_WAIT_FUNCTION WaitFunction OPTIONAL
,
321 IN BOOL CreateWaitBlock OPTIONAL
)
325 PGET_INPUT_INFO CapturedInputInfo
;
327 CapturedInputInfo
= RtlAllocateHeap(ConSrvHeap
, 0, sizeof(GET_INPUT_INFO
));
328 if (!CapturedInputInfo
) return STATUS_NO_MEMORY
;
330 RtlMoveMemory(CapturedInputInfo
, InputInfo
, sizeof(GET_INPUT_INFO
));
332 if (!CsrCreateWait(&InputInfo
->InputBuffer
->ReadWaitQueue
,
334 InputInfo
->CallingThread
,
339 RtlFreeHeap(ConSrvHeap
, 0, CapturedInputInfo
);
340 return STATUS_NO_MEMORY
;
345 return STATUS_PENDING
;
349 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo
,
351 IN PCSR_API_MESSAGE ApiMessage
,
352 IN BOOL CreateWaitBlock OPTIONAL
);
354 // Wait function CSR_WAIT_FUNCTION
356 ReadInputBufferThread(IN PLIST_ENTRY WaitList
,
357 IN PCSR_THREAD WaitThread
,
358 IN PCSR_API_MESSAGE WaitApiMessage
,
359 IN PVOID WaitContext
,
360 IN PVOID WaitArgument1
,
361 IN PVOID WaitArgument2
,
365 PCONSOLE_GETINPUT GetInputRequest
= &((PCONSOLE_API_MESSAGE
)WaitApiMessage
)->Data
.GetInputRequest
;
366 PGET_INPUT_INFO InputInfo
= (PGET_INPUT_INFO
)WaitContext
;
368 PVOID InputHandle
= WaitArgument2
;
370 DPRINT("ReadInputBufferThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext
, WaitArgument1
, WaitArgument2
, WaitFlags
);
373 * If we are notified of the process termination via a call
374 * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
375 * CsrDestroyThread, just return.
377 if (WaitFlags
& CsrProcessTerminating
)
379 Status
= STATUS_THREAD_IS_TERMINATING
;
384 * Somebody is closing a handle to this input buffer,
385 * by calling ConSrvCloseHandleEntry.
386 * See whether we are linked to that handle (ie. we
387 * are a waiter for this handle), and if so, return.
388 * Otherwise, ignore the call and continue waiting.
390 if (InputHandle
!= NULL
)
392 Status
= (InputHandle
== InputInfo
->HandleEntry
? STATUS_ALERTED
398 * If we go there, that means we are notified for some new input.
399 * The console is therefore already locked.
401 Status
= ReadInputBuffer(InputInfo
,
402 GetInputRequest
->bRead
,
407 if (Status
!= STATUS_PENDING
)
409 WaitApiMessage
->Status
= Status
;
410 RtlFreeHeap(ConSrvHeap
, 0, InputInfo
);
413 return (Status
== STATUS_PENDING
? FALSE
: TRUE
);
417 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo
,
418 IN BOOL Wait
, // TRUE --> Read ; FALSE --> Peek
419 IN PCSR_API_MESSAGE ApiMessage
,
420 IN BOOL CreateWaitBlock OPTIONAL
)
422 PCONSOLE_INPUT_BUFFER InputBuffer
= InputInfo
->InputBuffer
;
424 if (IsListEmpty(&InputBuffer
->InputEvents
))
428 return WaitBeforeReading(InputInfo
,
430 ReadInputBufferThread
,
435 /* No input available and we don't wait, so we return success */
436 return STATUS_SUCCESS
;
441 PCONSOLE_GETINPUT GetInputRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetInputRequest
;
442 PLIST_ENTRY CurrentInput
;
444 ULONG Length
= GetInputRequest
->Length
;
445 PINPUT_RECORD InputRecord
= GetInputRequest
->InputRecord
;
447 /* Only get input if there is any */
448 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
450 while ( CurrentInput
!= &InputBuffer
->InputEvents
&&
451 GetInputRequest
->InputsRead
< Length
)
453 Input
= CONTAINING_RECORD(CurrentInput
, ConsoleInput
, ListEntry
);
455 GetInputRequest
->InputsRead
++;
456 *InputRecord
= Input
->InputEvent
;
458 if (GetInputRequest
->Unicode
== FALSE
)
460 ConioInputEventToAnsi(InputBuffer
->Header
.Console
, InputRecord
);
464 CurrentInput
= CurrentInput
->Flink
;
466 if (Wait
) // TRUE --> Read, we remove inputs from the buffer ; FALSE --> Peek, we keep inputs.
468 RemoveEntryList(&Input
->ListEntry
);
469 RtlFreeHeap(ConSrvHeap
, 0, Input
);
473 if (IsListEmpty(&InputBuffer
->InputEvents
))
475 ResetEvent(InputBuffer
->ActiveEvent
);
478 /* We read all the inputs available, we return success */
479 return STATUS_SUCCESS
;
484 ReadChars(IN PGET_INPUT_INFO InputInfo
,
485 IN PCSR_API_MESSAGE ApiMessage
,
486 IN BOOL CreateWaitBlock OPTIONAL
);
488 // Wait function CSR_WAIT_FUNCTION
490 ReadCharsThread(IN PLIST_ENTRY WaitList
,
491 IN PCSR_THREAD WaitThread
,
492 IN PCSR_API_MESSAGE WaitApiMessage
,
493 IN PVOID WaitContext
,
494 IN PVOID WaitArgument1
,
495 IN PVOID WaitArgument2
,
499 PGET_INPUT_INFO InputInfo
= (PGET_INPUT_INFO
)WaitContext
;
501 PVOID InputHandle
= WaitArgument2
;
503 DPRINT("ReadCharsThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext
, WaitArgument1
, WaitArgument2
, WaitFlags
);
506 * If we are notified of the process termination via a call
507 * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
508 * CsrDestroyThread, just return.
510 if (WaitFlags
& CsrProcessTerminating
)
512 Status
= STATUS_THREAD_IS_TERMINATING
;
517 * Somebody is closing a handle to this input buffer,
518 * by calling ConSrvCloseHandleEntry.
519 * See whether we are linked to that handle (ie. we
520 * are a waiter for this handle), and if so, return.
521 * Otherwise, ignore the call and continue waiting.
523 if (InputHandle
!= NULL
)
525 Status
= (InputHandle
== InputInfo
->HandleEntry
? STATUS_ALERTED
531 * If we go there, that means we are notified for some new input.
532 * The console is therefore already locked.
534 Status
= ReadChars(InputInfo
,
539 if (Status
!= STATUS_PENDING
)
541 WaitApiMessage
->Status
= Status
;
542 RtlFreeHeap(ConSrvHeap
, 0, InputInfo
);
545 return (Status
== STATUS_PENDING
? FALSE
: TRUE
);
549 ReadChars(IN PGET_INPUT_INFO InputInfo
,
550 IN PCSR_API_MESSAGE ApiMessage
,
551 IN BOOL CreateWaitBlock OPTIONAL
)
553 BOOL WaitForMoreToRead
= TRUE
; // TRUE : Wait if more to read ; FALSE : Don't wait.
555 PCONSOLE_READCONSOLE ReadConsoleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ReadConsoleRequest
;
556 PCONSOLE_INPUT_BUFFER InputBuffer
= InputInfo
->InputBuffer
;
557 PCONSOLE Console
= InputBuffer
->Header
.Console
;
558 PLIST_ENTRY CurrentEntry
;
560 PCHAR Buffer
= (PCHAR
)ReadConsoleRequest
->Buffer
;
561 PWCHAR UnicodeBuffer
= (PWCHAR
)Buffer
;
562 ULONG nNumberOfCharsToRead
= ReadConsoleRequest
->NrCharactersToRead
;
564 /* We haven't read anything (yet) */
566 if (InputBuffer
->Mode
& ENABLE_LINE_INPUT
)
568 if (Console
->LineBuffer
== NULL
)
570 /* Starting a new line */
571 Console
->LineMaxSize
= (WORD
)max(256, nNumberOfCharsToRead
);
572 Console
->LineBuffer
= RtlAllocateHeap(ConSrvHeap
, 0, Console
->LineMaxSize
* sizeof(WCHAR
));
573 if (Console
->LineBuffer
== NULL
)
575 return STATUS_NO_MEMORY
;
577 Console
->LineComplete
= FALSE
;
578 Console
->LineUpPressed
= FALSE
;
579 Console
->LineInsertToggle
= 0;
580 Console
->LineWakeupMask
= ReadConsoleRequest
->CtrlWakeupMask
;
581 Console
->LineSize
= ReadConsoleRequest
->NrCharactersRead
;
582 Console
->LinePos
= Console
->LineSize
;
585 * Pre-filling the buffer is only allowed in the Unicode API,
586 * so we don't need to worry about ANSI <-> Unicode conversion.
588 memcpy(Console
->LineBuffer
, Buffer
, Console
->LineSize
* sizeof(WCHAR
));
589 if (Console
->LineSize
== Console
->LineMaxSize
)
591 Console
->LineComplete
= TRUE
;
592 Console
->LinePos
= 0;
596 /* If we don't have a complete line yet, process the pending input */
597 while ( !Console
->LineComplete
&&
598 !IsListEmpty(&InputBuffer
->InputEvents
) )
600 /* Remove input event from queue */
601 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
602 if (IsListEmpty(&InputBuffer
->InputEvents
))
604 ResetEvent(InputBuffer
->ActiveEvent
);
606 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
608 /* Only pay attention to key down */
609 if (KEY_EVENT
== Input
->InputEvent
.EventType
610 && Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
)
612 LineInputKeyDown(Console
, &Input
->InputEvent
.Event
.KeyEvent
);
613 ReadConsoleRequest
->ControlKeyState
= Input
->InputEvent
.Event
.KeyEvent
.dwControlKeyState
;
615 RtlFreeHeap(ConSrvHeap
, 0, Input
);
618 /* Check if we have a complete line to read from */
619 if (Console
->LineComplete
)
621 while ( ReadConsoleRequest
->NrCharactersRead
< nNumberOfCharsToRead
&&
622 Console
->LinePos
!= Console
->LineSize
)
624 WCHAR Char
= Console
->LineBuffer
[Console
->LinePos
++];
626 if (ReadConsoleRequest
->Unicode
)
628 UnicodeBuffer
[ReadConsoleRequest
->NrCharactersRead
] = Char
;
632 ConsoleInputUnicodeCharToAnsiChar(Console
,
633 &Buffer
[ReadConsoleRequest
->NrCharactersRead
],
637 ReadConsoleRequest
->NrCharactersRead
++;
640 if (Console
->LinePos
== Console
->LineSize
)
642 /* Entire line has been read */
643 RtlFreeHeap(ConSrvHeap
, 0, Console
->LineBuffer
);
644 Console
->LineBuffer
= NULL
;
647 WaitForMoreToRead
= FALSE
;
652 /* Character input */
653 while ( ReadConsoleRequest
->NrCharactersRead
< nNumberOfCharsToRead
&&
654 !IsListEmpty(&InputBuffer
->InputEvents
) )
656 /* Remove input event from queue */
657 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
658 if (IsListEmpty(&InputBuffer
->InputEvents
))
660 ResetEvent(InputBuffer
->ActiveEvent
);
662 Input
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
664 /* Only pay attention to valid ascii chars, on key down */
665 if (KEY_EVENT
== Input
->InputEvent
.EventType
666 && Input
->InputEvent
.Event
.KeyEvent
.bKeyDown
667 && Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
!= L
'\0')
669 WCHAR Char
= Input
->InputEvent
.Event
.KeyEvent
.uChar
.UnicodeChar
;
671 if (ReadConsoleRequest
->Unicode
)
673 UnicodeBuffer
[ReadConsoleRequest
->NrCharactersRead
] = Char
;
677 ConsoleInputUnicodeCharToAnsiChar(Console
,
678 &Buffer
[ReadConsoleRequest
->NrCharactersRead
],
682 ReadConsoleRequest
->NrCharactersRead
++;
684 /* Did read something */
685 WaitForMoreToRead
= FALSE
;
687 RtlFreeHeap(ConSrvHeap
, 0, Input
);
691 /* We haven't completed a read, so start a wait */
692 if (WaitForMoreToRead
== TRUE
)
694 return WaitBeforeReading(InputInfo
,
699 else /* We read all what we wanted, we return success */
701 return STATUS_SUCCESS
;
706 /* PUBLIC SERVER APIS *********************************************************/
708 CSR_API(SrvReadConsole
)
711 PCONSOLE_READCONSOLE ReadConsoleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ReadConsoleRequest
;
712 PCONSOLE_PROCESS_DATA ProcessData
= ConsoleGetPerProcessData(CsrGetClientThread()->Process
);
714 PCONSOLE_INPUT_BUFFER InputBuffer
;
715 GET_INPUT_INFO InputInfo
;
717 DPRINT("SrvReadConsole\n");
719 if (!CsrValidateMessageBuffer(ApiMessage
,
720 (PVOID
*)&ReadConsoleRequest
->Buffer
,
721 ReadConsoleRequest
->BufferSize
,
724 return STATUS_INVALID_PARAMETER
;
727 if (ReadConsoleRequest
->NrCharactersRead
> ReadConsoleRequest
->NrCharactersToRead
)
729 return STATUS_INVALID_PARAMETER
;
732 Status
= ConSrvGetInputBufferAndHandleEntry(ProcessData
, ReadConsoleRequest
->InputHandle
, &InputBuffer
, &HandleEntry
, GENERIC_READ
, TRUE
);
733 if (!NT_SUCCESS(Status
)) return Status
;
735 ReadConsoleRequest
->NrCharactersRead
= 0;
737 InputInfo
.CallingThread
= CsrGetClientThread();
738 InputInfo
.HandleEntry
= HandleEntry
;
739 InputInfo
.InputBuffer
= InputBuffer
;
741 Status
= ReadChars(&InputInfo
,
745 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);
747 if (Status
== STATUS_PENDING
)
748 *ReplyCode
= CsrReplyPending
;
753 CSR_API(SrvGetConsoleInput
)
756 PCONSOLE_GETINPUT GetInputRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetInputRequest
;
757 PCONSOLE_PROCESS_DATA ProcessData
= ConsoleGetPerProcessData(CsrGetClientThread()->Process
);
759 PCONSOLE_INPUT_BUFFER InputBuffer
;
760 GET_INPUT_INFO InputInfo
;
762 DPRINT("SrvGetConsoleInput\n");
764 if (!CsrValidateMessageBuffer(ApiMessage
,
765 (PVOID
*)&GetInputRequest
->InputRecord
,
766 GetInputRequest
->Length
,
767 sizeof(INPUT_RECORD
)))
769 return STATUS_INVALID_PARAMETER
;
772 GetInputRequest
->InputsRead
= 0;
774 Status
= ConSrvGetInputBufferAndHandleEntry(ProcessData
, GetInputRequest
->InputHandle
, &InputBuffer
, &HandleEntry
, GENERIC_READ
, TRUE
);
775 if (!NT_SUCCESS(Status
)) return Status
;
777 InputInfo
.CallingThread
= CsrGetClientThread();
778 InputInfo
.HandleEntry
= HandleEntry
;
779 InputInfo
.InputBuffer
= InputBuffer
;
781 Status
= ReadInputBuffer(&InputInfo
,
782 GetInputRequest
->bRead
,
786 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);
788 if (Status
== STATUS_PENDING
)
789 *ReplyCode
= CsrReplyPending
;
794 CSR_API(SrvWriteConsoleInput
)
797 PCONSOLE_WRITEINPUT WriteInputRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.WriteInputRequest
;
798 PINPUT_RECORD InputRecord
;
799 PCONSOLE_PROCESS_DATA ProcessData
= ConsoleGetPerProcessData(CsrGetClientThread()->Process
);
800 PCONSOLE_INPUT_BUFFER InputBuffer
;
805 DPRINT("SrvWriteConsoleInput\n");
807 if (!CsrValidateMessageBuffer(ApiMessage
,
808 (PVOID
*)&WriteInputRequest
->InputRecord
,
809 WriteInputRequest
->Length
,
810 sizeof(INPUT_RECORD
)))
812 return STATUS_INVALID_PARAMETER
;
815 Status
= ConSrvGetInputBuffer(ProcessData
, WriteInputRequest
->InputHandle
, &InputBuffer
, GENERIC_WRITE
, TRUE
);
816 if (!NT_SUCCESS(Status
)) return Status
;
818 Console
= InputBuffer
->Header
.Console
;
819 InputRecord
= WriteInputRequest
->InputRecord
;
820 Length
= WriteInputRequest
->Length
;
822 for (i
= 0; i
< Length
&& NT_SUCCESS(Status
); i
++)
824 if (!WriteInputRequest
->Unicode
&&
825 InputRecord
->EventType
== KEY_EVENT
)
827 CHAR AsciiChar
= InputRecord
->Event
.KeyEvent
.uChar
.AsciiChar
;
828 ConsoleInputAnsiCharToUnicodeChar(Console
,
829 &InputRecord
->Event
.KeyEvent
.uChar
.UnicodeChar
,
833 Status
= ConioProcessInputEvent(Console
, InputRecord
++);
836 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);
838 WriteInputRequest
->Length
= i
;
843 CSR_API(SrvFlushConsoleInputBuffer
)
846 PCONSOLE_FLUSHINPUTBUFFER FlushInputBufferRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.FlushInputBufferRequest
;
847 PLIST_ENTRY CurrentEntry
;
848 PCONSOLE_INPUT_BUFFER InputBuffer
;
851 DPRINT("SrvFlushConsoleInputBuffer\n");
853 Status
= ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
854 FlushInputBufferRequest
->InputHandle
,
858 if (!NT_SUCCESS(Status
)) return Status
;
860 /* Discard all entries in the input event queue */
861 while (!IsListEmpty(&InputBuffer
->InputEvents
))
863 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
864 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
865 RtlFreeHeap(ConSrvHeap
, 0, Event
);
867 ResetEvent(InputBuffer
->ActiveEvent
);
869 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);
870 return STATUS_SUCCESS
;
873 CSR_API(SrvGetConsoleNumberOfInputEvents
)
876 PCONSOLE_GETNUMINPUTEVENTS GetNumInputEventsRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetNumInputEventsRequest
;
877 PCONSOLE_INPUT_BUFFER InputBuffer
;
878 PLIST_ENTRY CurrentInput
;
881 DPRINT("SrvGetConsoleNumberOfInputEvents\n");
883 Status
= ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), GetNumInputEventsRequest
->InputHandle
, &InputBuffer
, GENERIC_READ
, TRUE
);
884 if (!NT_SUCCESS(Status
)) return Status
;
886 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
887 /* GetNumInputEventsRequest->NumInputEvents = */ NumEvents
= 0;
889 /* If there are any events ... */
890 while (CurrentInput
!= &InputBuffer
->InputEvents
)
892 CurrentInput
= CurrentInput
->Flink
;
896 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);
898 GetNumInputEventsRequest
->NumInputEvents
= NumEvents
;
900 return STATUS_SUCCESS
;