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