[CONSRV]
[reactos.git] / win32ss / user / consrv / coninput.c
1 /*
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 I/O functions
6 * PROGRAMMERS:
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "consrv.h"
12 #include "conio.h"
13 #include "tuiconsole.h"
14
15 #define NDEBUG
16 #include <debug.h>
17
18
19 /* GLOBALS ********************************************************************/
20
21 #define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
22 WideCharToMultiByte((Console)->CodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
23
24 #define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
25 MultiByteToWideChar((Console)->CodePage, 0, (sChar), 1, (dWChar), 1)
26
27
28 typedef struct _GET_INPUT_INFO
29 {
30 PCSR_THREAD CallingThread; // The thread which called the input API.
31 PCONSOLE_IO_HANDLE HandleEntry; // The handle data associated with the wait thread.
32 PCONSOLE_INPUT_BUFFER InputBuffer; // The input buffer corresponding to the handle.
33 } GET_INPUT_INFO, *PGET_INPUT_INFO;
34
35
36 /* PRIVATE FUNCTIONS **********************************************************/
37
38 static VOID FASTCALL
39 ConioInputEventToAnsi(PCONSOLE Console, PINPUT_RECORD InputEvent)
40 {
41 if (InputEvent->EventType == KEY_EVENT)
42 {
43 WCHAR UnicodeChar = InputEvent->Event.KeyEvent.uChar.UnicodeChar;
44 InputEvent->Event.KeyEvent.uChar.UnicodeChar = 0;
45 ConsoleInputUnicodeCharToAnsiChar(Console,
46 &InputEvent->Event.KeyEvent.uChar.AsciiChar,
47 &UnicodeChar);
48 }
49 }
50
51 static NTSTATUS FASTCALL
52 ConioProcessChar(PCONSOLE Console,
53 PINPUT_RECORD InputEvent)
54 {
55 ConsoleInput *ConInRec;
56
57 /* Check for pause or unpause */
58 if (InputEvent->EventType == KEY_EVENT && InputEvent->Event.KeyEvent.bKeyDown)
59 {
60 WORD vk = InputEvent->Event.KeyEvent.wVirtualKeyCode;
61 if (!(Console->PauseFlags & PAUSED_FROM_KEYBOARD))
62 {
63 DWORD cks = InputEvent->Event.KeyEvent.dwControlKeyState;
64 if (Console->InputBuffer.Mode & ENABLE_LINE_INPUT &&
65 (vk == VK_PAUSE || (vk == 'S' &&
66 (cks & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) &&
67 !(cks & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)))))
68 {
69 ConioPause(Console, PAUSED_FROM_KEYBOARD);
70 return STATUS_SUCCESS;
71 }
72 }
73 else
74 {
75 if ((vk < VK_SHIFT || vk > VK_CAPITAL) && vk != VK_LWIN &&
76 vk != VK_RWIN && vk != VK_NUMLOCK && vk != VK_SCROLL)
77 {
78 ConioUnpause(Console, PAUSED_FROM_KEYBOARD);
79 return STATUS_SUCCESS;
80 }
81 }
82 }
83
84 /* add event to the queue */
85 ConInRec = RtlAllocateHeap(ConSrvHeap, 0, sizeof(ConsoleInput));
86 if (ConInRec == NULL)
87 return STATUS_INSUFFICIENT_RESOURCES;
88 ConInRec->InputEvent = *InputEvent;
89 InsertTailList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry);
90
91 SetEvent(Console->InputBuffer.ActiveEvent);
92 CsrNotifyWait(&Console->InputBuffer.ReadWaitQueue,
93 WaitAny,
94 NULL,
95 NULL);
96
97 return STATUS_SUCCESS;
98 }
99
100 static DWORD FASTCALL
101 ConioGetShiftState(PBYTE KeyState)
102 {
103 DWORD ssOut = 0;
104
105 if (KeyState[VK_CAPITAL] & 1)
106 ssOut |= CAPSLOCK_ON;
107
108 if (KeyState[VK_NUMLOCK] & 1)
109 ssOut |= NUMLOCK_ON;
110
111 if (KeyState[VK_SCROLL] & 1)
112 ssOut |= SCROLLLOCK_ON;
113
114 if (KeyState[VK_SHIFT] & 0x80)
115 ssOut |= SHIFT_PRESSED;
116
117 if (KeyState[VK_LCONTROL] & 0x80)
118 ssOut |= LEFT_CTRL_PRESSED;
119 if (KeyState[VK_RCONTROL] & 0x80)
120 ssOut |= RIGHT_CTRL_PRESSED;
121
122 if (KeyState[VK_LMENU] & 0x80)
123 ssOut |= LEFT_ALT_PRESSED;
124 if (KeyState[VK_RMENU] & 0x80)
125 ssOut |= RIGHT_ALT_PRESSED;
126
127 return ssOut;
128 }
129
130 VOID WINAPI
131 ConioProcessKey(MSG *msg, PCONSOLE Console, BOOL TextMode)
132 {
133 static BYTE KeyState[256] = { 0 };
134 /* MSDN mentions that you should use the last virtual key code received
135 * when putting a virtual key identity to a WM_CHAR message since multiple
136 * or translated keys may be involved. */
137 static UINT LastVirtualKey = 0;
138 DWORD ShiftState;
139 UINT RepeatCount;
140 WCHAR UnicodeChar;
141 UINT VirtualKeyCode;
142 UINT VirtualScanCode;
143 BOOL Down = FALSE;
144 INPUT_RECORD er;
145 BOOLEAN Fake; // synthesized, not a real event
146 BOOLEAN NotChar; // message should not be used to return a character
147
148 RepeatCount = 1;
149 VirtualScanCode = (msg->lParam >> 16) & 0xff;
150 Down = msg->message == WM_KEYDOWN || msg->message == WM_CHAR ||
151 msg->message == WM_SYSKEYDOWN || msg->message == WM_SYSCHAR;
152
153 GetKeyboardState(KeyState);
154 ShiftState = ConioGetShiftState(KeyState);
155
156 if (msg->message == WM_CHAR || msg->message == WM_SYSCHAR)
157 {
158 VirtualKeyCode = LastVirtualKey;
159 UnicodeChar = msg->wParam;
160 }
161 else
162 {
163 WCHAR Chars[2];
164 INT RetChars = 0;
165
166 VirtualKeyCode = msg->wParam;
167 RetChars = ToUnicodeEx(VirtualKeyCode,
168 VirtualScanCode,
169 KeyState,
170 Chars,
171 2,
172 0,
173 0);
174 UnicodeChar = (1 == RetChars ? Chars[0] : 0);
175 }
176
177 er.EventType = KEY_EVENT;
178 er.Event.KeyEvent.bKeyDown = Down;
179 er.Event.KeyEvent.wRepeatCount = RepeatCount;
180 er.Event.KeyEvent.uChar.UnicodeChar = UnicodeChar;
181 er.Event.KeyEvent.dwControlKeyState = ShiftState;
182 er.Event.KeyEvent.wVirtualKeyCode = VirtualKeyCode;
183 er.Event.KeyEvent.wVirtualScanCode = VirtualScanCode;
184
185 if (TextMode)
186 {
187 if (0 != (ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
188 && VK_TAB == VirtualKeyCode)
189 {
190 if (Down)
191 {
192 TuiSwapConsole(ShiftState & SHIFT_PRESSED ? -1 : 1);
193 }
194
195 return;
196 }
197 else if (VK_MENU == VirtualKeyCode && ! Down)
198 {
199 if (TuiSwapConsole(0))
200 {
201 return;
202 }
203 }
204 }
205 else
206 {
207 if ((ShiftState & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED) || KeyState[VK_MENU] & 0x80) &&
208 (VirtualKeyCode == VK_ESCAPE || VirtualKeyCode == VK_TAB || VirtualKeyCode == VK_SPACE))
209 {
210 DefWindowProcW(msg->hwnd, msg->message, msg->wParam, msg->lParam);
211 return;
212 }
213 }
214
215 if (NULL == Console)
216 {
217 DPRINT1("No Active Console!\n");
218 return;
219 }
220
221 Fake = UnicodeChar &&
222 (msg->message != WM_CHAR && msg->message != WM_SYSCHAR &&
223 msg->message != WM_KEYUP && msg->message != WM_SYSKEYUP);
224 NotChar = (msg->message != WM_CHAR && msg->message != WM_SYSCHAR);
225 if (NotChar)
226 LastVirtualKey = msg->wParam;
227
228 DPRINT("CONSRV: %s %s %s %s %02x %02x '%lc' %04x\n",
229 Down ? "down" : "up ",
230 (msg->message == WM_CHAR || msg->message == WM_SYSCHAR) ?
231 "char" : "key ",
232 Fake ? "fake" : "real",
233 NotChar ? "notc" : "char",
234 VirtualScanCode,
235 VirtualKeyCode,
236 (UnicodeChar >= L' ') ? UnicodeChar : L'.',
237 ShiftState);
238
239 if (Fake)
240 return;
241
242 /* process Ctrl-C and Ctrl-Break */
243 if (Console->InputBuffer.Mode & ENABLE_PROCESSED_INPUT &&
244 er.Event.KeyEvent.bKeyDown &&
245 ((er.Event.KeyEvent.wVirtualKeyCode == VK_PAUSE) ||
246 (er.Event.KeyEvent.wVirtualKeyCode == 'C')) &&
247 (er.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) || KeyState[VK_CONTROL] & 0x80))
248 {
249 PCONSOLE_PROCESS_DATA current;
250 PLIST_ENTRY current_entry;
251
252 DPRINT1("Console_Api Ctrl-C\n");
253
254 current_entry = Console->ProcessList.Flink;
255 while (current_entry != &Console->ProcessList)
256 {
257 current = CONTAINING_RECORD(current_entry, CONSOLE_PROCESS_DATA, ConsoleLink);
258 current_entry = current_entry->Flink;
259 ConioConsoleCtrlEvent(CTRL_C_EVENT, current);
260 }
261 if (Console->LineBuffer && !Console->LineComplete)
262 {
263 /* Line input is in progress; end it */
264 Console->LinePos = Console->LineSize = 0;
265 Console->LineComplete = TRUE;
266 }
267 return;
268 }
269
270 if (0 != (er.Event.KeyEvent.dwControlKeyState
271 & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
272 && (VK_UP == er.Event.KeyEvent.wVirtualKeyCode
273 || VK_DOWN == er.Event.KeyEvent.wVirtualKeyCode))
274 {
275 if (er.Event.KeyEvent.bKeyDown)
276 {
277 /* scroll up or down */
278 if (VK_UP == er.Event.KeyEvent.wVirtualKeyCode)
279 {
280 /* only scroll up if there is room to scroll up into */
281 if (Console->ActiveBuffer->CurrentY != Console->ActiveBuffer->MaxY - 1)
282 {
283 Console->ActiveBuffer->VirtualY = (Console->ActiveBuffer->VirtualY +
284 Console->ActiveBuffer->MaxY - 1) %
285 Console->ActiveBuffer->MaxY;
286 Console->ActiveBuffer->CurrentY++;
287 }
288 }
289 else
290 {
291 /* only scroll down if there is room to scroll down into */
292 if (Console->ActiveBuffer->CurrentY != 0)
293 {
294 Console->ActiveBuffer->VirtualY = (Console->ActiveBuffer->VirtualY + 1) %
295 Console->ActiveBuffer->MaxY;
296 Console->ActiveBuffer->CurrentY--;
297 }
298 }
299 ConioDrawConsole(Console);
300 }
301 return;
302 }
303 ConioProcessChar(Console, &er);
304 }
305
306 static NTSTATUS
307 WaitBeforeReading(IN PGET_INPUT_INFO InputInfo,
308 IN PCSR_API_MESSAGE ApiMessage,
309 IN CSR_WAIT_FUNCTION WaitFunction OPTIONAL,
310 IN BOOL CreateWaitBlock OPTIONAL)
311 {
312 if (CreateWaitBlock)
313 {
314 PGET_INPUT_INFO CapturedInputInfo;
315
316 CapturedInputInfo = RtlAllocateHeap(ConSrvHeap, 0, sizeof(GET_INPUT_INFO));
317 if (!CapturedInputInfo) return STATUS_NO_MEMORY;
318
319 RtlMoveMemory(CapturedInputInfo, InputInfo, sizeof(GET_INPUT_INFO));
320
321 if (!CsrCreateWait(&InputInfo->InputBuffer->ReadWaitQueue,
322 WaitFunction,
323 InputInfo->CallingThread,
324 ApiMessage,
325 CapturedInputInfo,
326 NULL))
327 {
328 RtlFreeHeap(ConSrvHeap, 0, CapturedInputInfo);
329 return STATUS_NO_MEMORY;
330 }
331 }
332
333 /* Wait for input */
334 return STATUS_PENDING;
335 }
336
337 static NTSTATUS
338 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo,
339 IN BOOL Wait,
340 IN PCSR_API_MESSAGE ApiMessage,
341 IN BOOL CreateWaitBlock OPTIONAL);
342
343 // Wait function CSR_WAIT_FUNCTION
344 static BOOLEAN
345 ReadInputBufferThread(IN PLIST_ENTRY WaitList,
346 IN PCSR_THREAD WaitThread,
347 IN PCSR_API_MESSAGE WaitApiMessage,
348 IN PVOID WaitContext,
349 IN PVOID WaitArgument1,
350 IN PVOID WaitArgument2,
351 IN ULONG WaitFlags)
352 {
353 NTSTATUS Status;
354 PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)WaitApiMessage)->Data.GetInputRequest;
355 PGET_INPUT_INFO InputInfo = (PGET_INPUT_INFO)WaitContext;
356
357 PCONSOLE_IO_HANDLE InputHandle = (PCONSOLE_IO_HANDLE)WaitArgument2;
358
359 DPRINT1("ReadInputBufferThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext, WaitArgument1, WaitArgument2, WaitFlags);
360
361 /*
362 * Somebody is closing a handle to this input buffer,
363 * by calling Win32CsrCloseHandleEntry.
364 * See whether we are linked to that handle (ie. we
365 * are a waiter for this handle), and if so, return.
366 */
367 if (InputHandle == InputInfo->HandleEntry)
368 {
369 Status = STATUS_ALERTED;
370 goto Quit;
371 }
372
373 /*
374 * If we are notified of the process termination via a call
375 * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
376 * CsrDestroyThread, just return.
377 */
378 if (WaitFlags & CsrProcessTerminating)
379 {
380 Status = STATUS_THREAD_IS_TERMINATING;
381 goto Quit;
382 }
383
384 /*
385 * If we go there, that means we are notified for some new input.
386 * The console is therefore already locked.
387 */
388 Status = ReadInputBuffer(InputInfo,
389 GetInputRequest->bRead,
390 WaitApiMessage,
391 FALSE);
392
393 Quit:
394 if (Status != STATUS_PENDING)
395 {
396 WaitApiMessage->Status = Status;
397 RtlFreeHeap(ConSrvHeap, 0, InputInfo);
398 }
399
400 return (Status == STATUS_PENDING ? FALSE : TRUE);
401 }
402
403 static NTSTATUS
404 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo,
405 IN BOOL Wait, // TRUE --> Read ; FALSE --> Peek
406 IN PCSR_API_MESSAGE ApiMessage,
407 IN BOOL CreateWaitBlock OPTIONAL)
408 {
409 PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer;
410
411 if (IsListEmpty(&InputBuffer->InputEvents))
412 {
413 if (Wait)
414 {
415 return WaitBeforeReading(InputInfo,
416 ApiMessage,
417 ReadInputBufferThread,
418 CreateWaitBlock);
419 }
420 else
421 {
422 /* No input available and we don't wait, so we return success */
423 return STATUS_SUCCESS;
424 }
425 }
426 else
427 {
428 PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetInputRequest;
429 PLIST_ENTRY CurrentInput;
430 ConsoleInput* Input;
431 ULONG Length = GetInputRequest->Length;
432 PINPUT_RECORD InputRecord = GetInputRequest->InputRecord;
433
434 /* Only get input if there is any */
435 CurrentInput = InputBuffer->InputEvents.Flink;
436
437 while ( CurrentInput != &InputBuffer->InputEvents &&
438 GetInputRequest->InputsRead < Length )
439 {
440 Input = CONTAINING_RECORD(CurrentInput, ConsoleInput, ListEntry);
441
442 GetInputRequest->InputsRead++;
443 *InputRecord = Input->InputEvent;
444
445 if (GetInputRequest->Unicode == FALSE)
446 {
447 ConioInputEventToAnsi(InputBuffer->Header.Console, InputRecord);
448 }
449
450 InputRecord++;
451 CurrentInput = CurrentInput->Flink;
452
453 if (Wait) // TRUE --> Read, we remove inputs from the buffer ; FALSE --> Peek, we keep inputs.
454 {
455 RemoveEntryList(&Input->ListEntry);
456 RtlFreeHeap(ConSrvHeap, 0, Input);
457 }
458 }
459
460 if (IsListEmpty(&InputBuffer->InputEvents))
461 {
462 ResetEvent(InputBuffer->ActiveEvent);
463 }
464
465 /* We read all the inputs available, we return success */
466 return STATUS_SUCCESS;
467 }
468 }
469
470 static NTSTATUS
471 ReadChars(IN PGET_INPUT_INFO InputInfo,
472 IN PCSR_API_MESSAGE ApiMessage,
473 IN BOOL CreateWaitBlock OPTIONAL);
474
475 // Wait function CSR_WAIT_FUNCTION
476 static BOOLEAN
477 ReadCharsThread(IN PLIST_ENTRY WaitList,
478 IN PCSR_THREAD WaitThread,
479 IN PCSR_API_MESSAGE WaitApiMessage,
480 IN PVOID WaitContext,
481 IN PVOID WaitArgument1,
482 IN PVOID WaitArgument2,
483 IN ULONG WaitFlags)
484 {
485 NTSTATUS Status;
486 PGET_INPUT_INFO InputInfo = (PGET_INPUT_INFO)WaitContext;
487
488 PCONSOLE_IO_HANDLE InputHandle = (PCONSOLE_IO_HANDLE)WaitArgument2;
489
490 DPRINT1("ReadCharsThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext, WaitArgument1, WaitArgument2, WaitFlags);
491
492 /*
493 * Somebody is closing a handle to this input buffer,
494 * by calling Win32CsrCloseHandleEntry.
495 * See whether we are linked to that handle (ie. we
496 * are a waiter for this handle), and if so, return.
497 */
498 if (InputHandle == InputInfo->HandleEntry)
499 {
500 Status = STATUS_ALERTED;
501 goto Quit;
502 }
503
504 /*
505 * If we are notified of the process termination via a call
506 * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
507 * CsrDestroyThread, just return.
508 */
509 if (WaitFlags & CsrProcessTerminating)
510 {
511 Status = STATUS_THREAD_IS_TERMINATING;
512 goto Quit;
513 }
514
515 /*
516 * If we go there, that means we are notified for some new input.
517 * The console is therefore already locked.
518 */
519 Status = ReadChars(InputInfo,
520 WaitApiMessage,
521 FALSE);
522
523 Quit:
524 if (Status != STATUS_PENDING)
525 {
526 WaitApiMessage->Status = Status;
527 RtlFreeHeap(ConSrvHeap, 0, InputInfo);
528 }
529
530 return (Status == STATUS_PENDING ? FALSE : TRUE);
531 }
532
533 static NTSTATUS
534 ReadChars(IN PGET_INPUT_INFO InputInfo,
535 IN PCSR_API_MESSAGE ApiMessage,
536 IN BOOL CreateWaitBlock OPTIONAL)
537 {
538 BOOL WaitForMoreToRead = TRUE; // TRUE : Wait if more to read ; FALSE : Don't wait.
539
540 PCONSOLE_READCONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest;
541 PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer;
542 PCONSOLE Console = InputBuffer->Header.Console;
543 PLIST_ENTRY CurrentEntry;
544 ConsoleInput *Input;
545 PCHAR Buffer = (PCHAR)ReadConsoleRequest->Buffer;
546 PWCHAR UnicodeBuffer = (PWCHAR)Buffer;
547 ULONG nNumberOfCharsToRead = ReadConsoleRequest->NrCharactersToRead;
548
549 /* We haven't read anything (yet) */
550
551 if (InputBuffer->Mode & ENABLE_LINE_INPUT)
552 {
553 if (Console->LineBuffer == NULL)
554 {
555 /* Starting a new line */
556 Console->LineMaxSize = max(256, nNumberOfCharsToRead);
557 Console->LineBuffer = RtlAllocateHeap(ConSrvHeap, 0, Console->LineMaxSize * sizeof(WCHAR));
558 if (Console->LineBuffer == NULL)
559 {
560 return STATUS_NO_MEMORY;
561 }
562 Console->LineComplete = FALSE;
563 Console->LineUpPressed = FALSE;
564 Console->LineInsertToggle = 0;
565 Console->LineWakeupMask = ReadConsoleRequest->CtrlWakeupMask;
566 Console->LineSize = ReadConsoleRequest->NrCharactersRead;
567 Console->LinePos = Console->LineSize;
568
569 /*
570 * Pre-filling the buffer is only allowed in the Unicode API,
571 * so we don't need to worry about ANSI <-> Unicode conversion.
572 */
573 memcpy(Console->LineBuffer, Buffer, Console->LineSize * sizeof(WCHAR));
574 if (Console->LineSize == Console->LineMaxSize)
575 {
576 Console->LineComplete = TRUE;
577 Console->LinePos = 0;
578 }
579 }
580
581 /* If we don't have a complete line yet, process the pending input */
582 while ( !Console->LineComplete &&
583 !IsListEmpty(&InputBuffer->InputEvents) )
584 {
585 /* Remove input event from queue */
586 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
587 if (IsListEmpty(&InputBuffer->InputEvents))
588 {
589 ResetEvent(InputBuffer->ActiveEvent);
590 }
591 Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
592
593 /* Only pay attention to key down */
594 if (KEY_EVENT == Input->InputEvent.EventType
595 && Input->InputEvent.Event.KeyEvent.bKeyDown)
596 {
597 LineInputKeyDown(Console, &Input->InputEvent.Event.KeyEvent);
598 ReadConsoleRequest->ControlKeyState = Input->InputEvent.Event.KeyEvent.dwControlKeyState;
599 }
600 RtlFreeHeap(ConSrvHeap, 0, Input);
601 }
602
603 /* Check if we have a complete line to read from */
604 if (Console->LineComplete)
605 {
606 while ( ReadConsoleRequest->NrCharactersRead < nNumberOfCharsToRead &&
607 Console->LinePos != Console->LineSize )
608 {
609 WCHAR Char = Console->LineBuffer[Console->LinePos++];
610
611 if (ReadConsoleRequest->Unicode)
612 {
613 UnicodeBuffer[ReadConsoleRequest->NrCharactersRead] = Char;
614 }
615 else
616 {
617 ConsoleInputUnicodeCharToAnsiChar(Console,
618 &Buffer[ReadConsoleRequest->NrCharactersRead],
619 &Char);
620 }
621
622 ReadConsoleRequest->NrCharactersRead++;
623 }
624
625 if (Console->LinePos == Console->LineSize)
626 {
627 /* Entire line has been read */
628 RtlFreeHeap(ConSrvHeap, 0, Console->LineBuffer);
629 Console->LineBuffer = NULL;
630 }
631
632 WaitForMoreToRead = FALSE;
633 }
634 }
635 else
636 {
637 /* Character input */
638 while ( ReadConsoleRequest->NrCharactersRead < nNumberOfCharsToRead &&
639 !IsListEmpty(&InputBuffer->InputEvents) )
640 {
641 /* Remove input event from queue */
642 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
643 if (IsListEmpty(&InputBuffer->InputEvents))
644 {
645 ResetEvent(InputBuffer->ActiveEvent);
646 }
647 Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
648
649 /* Only pay attention to valid ascii chars, on key down */
650 if (KEY_EVENT == Input->InputEvent.EventType
651 && Input->InputEvent.Event.KeyEvent.bKeyDown
652 && Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar != L'\0')
653 {
654 WCHAR Char = Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar;
655
656 if (ReadConsoleRequest->Unicode)
657 {
658 UnicodeBuffer[ReadConsoleRequest->NrCharactersRead] = Char;
659 }
660 else
661 {
662 ConsoleInputUnicodeCharToAnsiChar(Console,
663 &Buffer[ReadConsoleRequest->NrCharactersRead],
664 &Char);
665 }
666
667 ReadConsoleRequest->NrCharactersRead++;
668
669 /* Did read something */
670 WaitForMoreToRead = FALSE;
671 }
672 RtlFreeHeap(ConSrvHeap, 0, Input);
673 }
674 }
675
676 /* We haven't completed a read, so start a wait */
677 if (WaitForMoreToRead == TRUE)
678 {
679 return WaitBeforeReading(InputInfo,
680 ApiMessage,
681 ReadCharsThread,
682 CreateWaitBlock);
683 }
684 else /* We read all what we wanted, we return success */
685 {
686 return STATUS_SUCCESS;
687 }
688 }
689
690
691 /* PUBLIC APIS ****************************************************************/
692
693 CSR_API(SrvReadConsole)
694 {
695 NTSTATUS Status;
696 PCONSOLE_READCONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest;
697 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
698 PCONSOLE_IO_HANDLE HandleEntry;
699 PCONSOLE_INPUT_BUFFER InputBuffer;
700 GET_INPUT_INFO InputInfo;
701
702 DPRINT("SrvReadConsole\n");
703
704 if (!CsrValidateMessageBuffer(ApiMessage,
705 (PVOID*)&ReadConsoleRequest->Buffer,
706 ReadConsoleRequest->BufferSize,
707 sizeof(BYTE)))
708 {
709 return STATUS_INVALID_PARAMETER;
710 }
711
712 // if (Request->Data.ReadConsoleRequest.NrCharactersRead * sizeof(WCHAR) > nNumberOfCharsToRead * CharSize)
713 if (ReadConsoleRequest->NrCharactersRead > ReadConsoleRequest->NrCharactersToRead)
714 {
715 return STATUS_INVALID_PARAMETER;
716 }
717
718 Status = ConioGetInputBufferAndHandleEntry(ProcessData, ReadConsoleRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE);
719 if (!NT_SUCCESS(Status)) return Status;
720
721 ReadConsoleRequest->NrCharactersRead = 0;
722
723 InputInfo.CallingThread = CsrGetClientThread();
724 InputInfo.HandleEntry = HandleEntry;
725 InputInfo.InputBuffer = InputBuffer;
726
727 Status = ReadChars(&InputInfo,
728 ApiMessage,
729 TRUE);
730
731 ConioReleaseInputBuffer(InputBuffer, TRUE);
732
733 if (Status == STATUS_PENDING)
734 *ReplyCode = CsrReplyPending;
735
736 return Status;
737 }
738
739 CSR_API(SrvGetConsoleInput)
740 {
741 NTSTATUS Status;
742 PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetInputRequest;
743 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
744 PCONSOLE_IO_HANDLE HandleEntry;
745 PCONSOLE_INPUT_BUFFER InputBuffer;
746 GET_INPUT_INFO InputInfo;
747
748 DPRINT("SrvGetConsoleInput\n");
749
750 if (!CsrValidateMessageBuffer(ApiMessage,
751 (PVOID*)&GetInputRequest->InputRecord,
752 GetInputRequest->Length,
753 sizeof(INPUT_RECORD)))
754 {
755 return STATUS_INVALID_PARAMETER;
756 }
757
758 GetInputRequest->InputsRead = 0;
759
760 Status = ConioGetInputBufferAndHandleEntry(ProcessData, GetInputRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE);
761 if(!NT_SUCCESS(Status)) return Status;
762
763 InputInfo.CallingThread = CsrGetClientThread();
764 InputInfo.HandleEntry = HandleEntry;
765 InputInfo.InputBuffer = InputBuffer;
766
767 Status = ReadInputBuffer(&InputInfo,
768 GetInputRequest->bRead,
769 ApiMessage,
770 TRUE);
771
772 ConioReleaseInputBuffer(InputBuffer, TRUE);
773
774 if (Status == STATUS_PENDING)
775 *ReplyCode = CsrReplyPending;
776
777 return Status;
778 }
779
780 CSR_API(SrvWriteConsoleInput)
781 {
782 NTSTATUS Status;
783 PCONSOLE_WRITEINPUT WriteInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteInputRequest;
784 PINPUT_RECORD InputRecord;
785 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
786 PCONSOLE_INPUT_BUFFER InputBuffer;
787 PCONSOLE Console;
788 DWORD Length;
789 DWORD i;
790
791 DPRINT("SrvWriteConsoleInput\n");
792
793 if (!CsrValidateMessageBuffer(ApiMessage,
794 (PVOID*)&WriteInputRequest->InputRecord,
795 WriteInputRequest->Length,
796 sizeof(INPUT_RECORD)))
797 {
798 return STATUS_INVALID_PARAMETER;
799 }
800
801 Status = ConioGetInputBuffer(ProcessData, WriteInputRequest->InputHandle, &InputBuffer, GENERIC_WRITE, TRUE);
802 if (!NT_SUCCESS(Status)) return Status;
803
804 Console = InputBuffer->Header.Console;
805 InputRecord = WriteInputRequest->InputRecord;
806 Length = WriteInputRequest->Length;
807
808 for (i = 0; i < Length && NT_SUCCESS(Status); i++)
809 {
810 if (!WriteInputRequest->Unicode &&
811 InputRecord->EventType == KEY_EVENT)
812 {
813 CHAR AsciiChar = InputRecord->Event.KeyEvent.uChar.AsciiChar;
814 ConsoleInputAnsiCharToUnicodeChar(Console,
815 &InputRecord->Event.KeyEvent.uChar.UnicodeChar,
816 &AsciiChar);
817 }
818
819 Status = ConioProcessChar(Console, InputRecord++);
820 }
821
822 ConioReleaseInputBuffer(InputBuffer, TRUE);
823
824 WriteInputRequest->Length = i;
825
826 return Status;
827 }
828
829 CSR_API(SrvFlushConsoleInputBuffer)
830 {
831 NTSTATUS Status;
832 PCONSOLE_FLUSHINPUTBUFFER FlushInputBufferRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.FlushInputBufferRequest;
833 PLIST_ENTRY CurrentEntry;
834 PCONSOLE_INPUT_BUFFER InputBuffer;
835 ConsoleInput* Input;
836
837 DPRINT("SrvFlushConsoleInputBuffer\n");
838
839 Status = ConioGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
840 FlushInputBufferRequest->InputHandle,
841 &InputBuffer,
842 GENERIC_WRITE,
843 TRUE);
844 if(!NT_SUCCESS(Status)) return Status;
845
846 /* Discard all entries in the input event queue */
847 while (!IsListEmpty(&InputBuffer->InputEvents))
848 {
849 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
850 Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
851 /* Destroy the event */
852 RtlFreeHeap(ConSrvHeap, 0, Input);
853 }
854 ResetEvent(InputBuffer->ActiveEvent);
855
856 ConioReleaseInputBuffer(InputBuffer, TRUE);
857
858 return STATUS_SUCCESS;
859 }
860
861 CSR_API(SrvGetConsoleNumberOfInputEvents)
862 {
863 NTSTATUS Status;
864 PCONSOLE_GETNUMINPUTEVENTS GetNumInputEventsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetNumInputEventsRequest;
865 PCONSOLE_INPUT_BUFFER InputBuffer;
866 PLIST_ENTRY CurrentInput;
867 DWORD NumEvents;
868
869 DPRINT("SrvGetConsoleNumberOfInputEvents\n");
870
871 Status = ConioGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process), GetNumInputEventsRequest->InputHandle, &InputBuffer, GENERIC_READ, TRUE);
872 if (!NT_SUCCESS(Status)) return Status;
873
874 CurrentInput = InputBuffer->InputEvents.Flink;
875 NumEvents = 0;
876
877 /* If there are any events ... */
878 while (CurrentInput != &InputBuffer->InputEvents)
879 {
880 CurrentInput = CurrentInput->Flink;
881 NumEvents++;
882 }
883
884 ConioReleaseInputBuffer(InputBuffer, TRUE);
885
886 GetNumInputEventsRequest->NumInputEvents = NumEvents;
887
888 return STATUS_SUCCESS;
889 }
890
891 /* EOF */