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