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