xref: /reactos/win32ss/user/winsrv/consrv/coninput.c (revision d8c6ef5e)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Console Server DLL
4  * FILE:            win32ss/user/winsrv/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 
14 #define NDEBUG
15 #include <debug.h>
16 
17 /* GLOBALS ********************************************************************/
18 
19 #define ConSrvGetInputBuffer(ProcessData, Handle, Ptr, Access, LockConsole)     \
20     ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), NULL,  \
21                     (Access), (LockConsole), INPUT_BUFFER)
22 
23 #define ConSrvGetInputBufferAndHandleEntry(ProcessData, Handle, Ptr, Entry, Access, LockConsole)    \
24     ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), (Entry),                   \
25                     (Access), (LockConsole), INPUT_BUFFER)
26 
27 #define ConSrvReleaseInputBuffer(Buff, IsConsoleLocked) \
28     ConSrvReleaseObject(&(Buff)->Header, (IsConsoleLocked))
29 
30 
31 /*
32  * From MSDN:
33  * "The lpMultiByteStr and lpWideCharStr pointers must not be the same.
34  *  If they are the same, the function fails, and GetLastError returns
35  *  ERROR_INVALID_PARAMETER."
36  */
37 #define ConsoleInputUnicodeToAnsiChar(Console, dChar, sWChar) \
38 do { \
39     ASSERT((ULONG_PTR)(dChar) != (ULONG_PTR)(sWChar)); \
40     WideCharToMultiByte((Console)->InputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL); \
41 } while (0)
42 
43 #define ConsoleInputAnsiToUnicodeChar(Console, dWChar, sChar) \
44 do { \
45     ASSERT((ULONG_PTR)(dWChar) != (ULONG_PTR)(sChar)); \
46     MultiByteToWideChar((Console)->InputCodePage, 0, (sChar), 1, (dWChar), 1); \
47 } while (0)
48 
49 
50 typedef struct _GET_INPUT_INFO
51 {
52     PCSR_THREAD           CallingThread;    // The thread which called the input API.
53     PVOID                 HandleEntry;      // The handle data associated with the wait thread.
54     PCONSOLE_INPUT_BUFFER InputBuffer;      // The input buffer corresponding to the handle.
55 } GET_INPUT_INFO, *PGET_INPUT_INFO;
56 
57 
58 /* PRIVATE FUNCTIONS **********************************************************/
59 
60 static VOID
61 ConioInputEventToAnsi(PCONSOLE Console, PINPUT_RECORD InputEvent)
62 {
63     if (InputEvent->EventType == KEY_EVENT)
64     {
65         WCHAR UnicodeChar = InputEvent->Event.KeyEvent.uChar.UnicodeChar;
66         InputEvent->Event.KeyEvent.uChar.UnicodeChar = 0;
67         ConsoleInputUnicodeToAnsiChar(Console,
68                                       &InputEvent->Event.KeyEvent.uChar.AsciiChar,
69                                       &UnicodeChar);
70     }
71 }
72 
73 static VOID
74 ConioInputEventToUnicode(PCONSOLE Console, PINPUT_RECORD InputEvent)
75 {
76     if (InputEvent->EventType == KEY_EVENT)
77     {
78         CHAR AsciiChar = InputEvent->Event.KeyEvent.uChar.AsciiChar;
79         InputEvent->Event.KeyEvent.uChar.AsciiChar = 0;
80         ConsoleInputAnsiToUnicodeChar(Console,
81                                       &InputEvent->Event.KeyEvent.uChar.UnicodeChar,
82                                       &AsciiChar);
83     }
84 }
85 
86 static ULONG
87 PreprocessInput(PCONSRV_CONSOLE Console,
88                 PINPUT_RECORD InputEvent,
89                 ULONG NumEventsToWrite)
90 {
91     ULONG NumEvents;
92 
93     /*
94      * Loop each event, and for each, check for pause or unpause
95      * and perform adequate behaviour.
96      */
97     for (NumEvents = NumEventsToWrite; NumEvents > 0; --NumEvents)
98     {
99         /* Check for pause or unpause */
100         if (InputEvent->EventType == KEY_EVENT && InputEvent->Event.KeyEvent.bKeyDown)
101         {
102             WORD vk = InputEvent->Event.KeyEvent.wVirtualKeyCode;
103             if (!(Console->PauseFlags & PAUSED_FROM_KEYBOARD))
104             {
105                 DWORD cks = InputEvent->Event.KeyEvent.dwControlKeyState;
106                 if (Console->InputBuffer.Mode & ENABLE_LINE_INPUT &&
107                     (vk == VK_PAUSE ||
108                     (vk == 'S' && (cks & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) &&
109                                  !(cks & (LEFT_ALT_PRESSED  | RIGHT_ALT_PRESSED)))))
110                 {
111                     ConioPause(Console, PAUSED_FROM_KEYBOARD);
112 
113                     /* Skip the event */
114                     RtlMoveMemory(InputEvent,
115                                   InputEvent + 1,
116                                   (NumEvents - 1) * sizeof(INPUT_RECORD));
117                     --NumEventsToWrite;
118                     continue;
119                 }
120             }
121             else
122             {
123                 if ((vk < VK_SHIFT || vk > VK_CAPITAL) && vk != VK_LWIN &&
124                     vk != VK_RWIN && vk != VK_NUMLOCK && vk != VK_SCROLL)
125                 {
126                     ConioUnpause(Console, PAUSED_FROM_KEYBOARD);
127 
128                     /* Skip the event */
129                     RtlMoveMemory(InputEvent,
130                                   InputEvent + 1,
131                                   (NumEvents - 1) * sizeof(INPUT_RECORD));
132                     --NumEventsToWrite;
133                     continue;
134                 }
135             }
136         }
137 
138         /* Go to the next event */
139         ++InputEvent;
140     }
141 
142     return NumEventsToWrite;
143 }
144 
145 static VOID
146 PostprocessInput(PCONSRV_CONSOLE Console)
147 {
148     CsrNotifyWait(&Console->ReadWaitQueue,
149                   FALSE,
150                   NULL,
151                   NULL);
152     if (!IsListEmpty(&Console->ReadWaitQueue))
153     {
154         CsrDereferenceWait(&Console->ReadWaitQueue);
155     }
156 }
157 
158 
159 NTSTATUS NTAPI
160 ConDrvWriteConsoleInput(IN PCONSOLE Console,
161                         IN PCONSOLE_INPUT_BUFFER InputBuffer,
162                         IN BOOLEAN AppendToEnd,
163                         IN PINPUT_RECORD InputRecord,
164                         IN ULONG NumEventsToWrite,
165                         OUT PULONG NumEventsWritten OPTIONAL);
166 static NTSTATUS
167 ConioAddInputEvents(PCONSRV_CONSOLE Console,
168                     PINPUT_RECORD InputRecords, // InputEvent
169                     ULONG NumEventsToWrite,
170                     PULONG NumEventsWritten,
171                     BOOLEAN AppendToEnd)
172 {
173     NTSTATUS Status = STATUS_SUCCESS;
174 
175     if (NumEventsWritten) *NumEventsWritten = 0;
176 
177     NumEventsToWrite = PreprocessInput(Console, InputRecords, NumEventsToWrite);
178     if (NumEventsToWrite == 0) return STATUS_SUCCESS;
179 
180     // Status = ConDrvAddInputEvents(Console,
181                                   // InputRecords,
182                                   // NumEventsToWrite,
183                                   // NumEventsWritten,
184                                   // AppendToEnd);
185 
186     Status = ConDrvWriteConsoleInput((PCONSOLE)Console,
187                                      &Console->InputBuffer,
188                                      AppendToEnd,
189                                      InputRecords,
190                                      NumEventsToWrite,
191                                      NumEventsWritten);
192 
193     // if (NT_SUCCESS(Status))
194     if (Status == STATUS_SUCCESS) PostprocessInput(Console);
195 
196     return Status;
197 }
198 
199 /* FIXME: This function can be called by CONDRV, in ConioResizeBuffer() in text.c */
200 NTSTATUS
201 ConioProcessInputEvent(PCONSRV_CONSOLE Console,
202                        PINPUT_RECORD InputEvent)
203 {
204     ULONG NumEventsWritten;
205 
206     if (InputEvent->EventType == KEY_EVENT)
207     {
208         BOOL Down = InputEvent->Event.KeyEvent.bKeyDown;
209         UINT VirtualKeyCode = InputEvent->Event.KeyEvent.wVirtualKeyCode;
210         DWORD ShiftState = InputEvent->Event.KeyEvent.dwControlKeyState;
211 
212         /* Process Ctrl-C and Ctrl-Break */
213         if ( (GetConsoleInputBufferMode(Console) & ENABLE_PROCESSED_INPUT) &&
214              Down && (VirtualKeyCode == VK_PAUSE || VirtualKeyCode == 'C') &&
215              (ShiftState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) )
216         {
217             DPRINT1("Console_Api Ctrl-C\n");
218             ConSrvConsoleProcessCtrlEvent(Console, 0, CTRL_C_EVENT);
219 
220             if (Console->LineBuffer && !Console->LineComplete)
221             {
222                 /* Line input is in progress; end it */
223                 Console->LinePos = Console->LineSize = 0;
224                 Console->LineComplete = TRUE;
225             }
226             return STATUS_SUCCESS; // STATUS_CONTROL_C_EXIT;
227         }
228     }
229 
230     return ConioAddInputEvents(Console,
231                                InputEvent,
232                                1,
233                                &NumEventsWritten,
234                                TRUE);
235 }
236 
237 
238 static NTSTATUS
239 WaitBeforeReading(IN PGET_INPUT_INFO InputInfo,
240                   IN PCSR_API_MESSAGE ApiMessage,
241                   IN CSR_WAIT_FUNCTION WaitFunction OPTIONAL,
242                   IN BOOLEAN CreateWaitBlock OPTIONAL)
243 {
244     if (CreateWaitBlock)
245     {
246         PGET_INPUT_INFO CapturedInputInfo;
247         PCONSRV_CONSOLE Console = (PCONSRV_CONSOLE)InputInfo->InputBuffer->Header.Console;
248 
249         CapturedInputInfo = ConsoleAllocHeap(0, sizeof(GET_INPUT_INFO));
250         if (!CapturedInputInfo) return STATUS_NO_MEMORY;
251 
252         RtlMoveMemory(CapturedInputInfo, InputInfo, sizeof(GET_INPUT_INFO));
253 
254         if (!CsrCreateWait(&Console->ReadWaitQueue,
255                            WaitFunction,
256                            InputInfo->CallingThread,
257                            ApiMessage,
258                            CapturedInputInfo))
259         {
260             ConsoleFreeHeap(CapturedInputInfo);
261             return STATUS_NO_MEMORY;
262         }
263     }
264 
265     /* Wait for input */
266     return STATUS_PENDING;
267 }
268 
269 static NTSTATUS
270 ReadChars(IN PGET_INPUT_INFO InputInfo,
271           IN PCSR_API_MESSAGE ApiMessage,
272           IN BOOLEAN CreateWaitBlock OPTIONAL);
273 
274 // Wait function CSR_WAIT_FUNCTION
275 static BOOLEAN
276 NTAPI
277 ReadCharsThread(IN PLIST_ENTRY WaitList,
278                 IN PCSR_THREAD WaitThread,
279                 IN PCSR_API_MESSAGE WaitApiMessage,
280                 IN PVOID WaitContext,
281                 IN PVOID WaitArgument1,
282                 IN PVOID WaitArgument2,
283                 IN ULONG WaitFlags)
284 {
285     NTSTATUS Status;
286     PGET_INPUT_INFO InputInfo = (PGET_INPUT_INFO)WaitContext;
287 
288     PVOID InputHandle = WaitArgument2;
289 
290     DPRINT("ReadCharsThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext, WaitArgument1, WaitArgument2, WaitFlags);
291 
292     /*
293      * If we are notified of the process termination via a call
294      * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
295      * CsrDestroyThread, just return.
296      */
297     if (WaitFlags & CsrProcessTerminating)
298     {
299         Status = STATUS_THREAD_IS_TERMINATING;
300         goto Quit;
301     }
302 
303     /*
304      * Somebody is closing a handle to this input buffer,
305      * by calling ConSrvCloseHandleEntry.
306      * See whether we are linked to that handle (ie. we
307      * are a waiter for this handle), and if so, return.
308      * Otherwise, ignore the call and continue waiting.
309      */
310     if (InputHandle != NULL)
311     {
312         Status = (InputHandle == InputInfo->HandleEntry ? STATUS_ALERTED
313                                                         : STATUS_PENDING);
314         goto Quit;
315     }
316 
317     /*
318      * If we go there, that means we are notified for some new input.
319      * The console is therefore already locked.
320      */
321     Status = ReadChars(InputInfo, WaitApiMessage, FALSE);
322 
323 Quit:
324     if (Status != STATUS_PENDING)
325     {
326         WaitApiMessage->Status = Status;
327         ConsoleFreeHeap(InputInfo);
328     }
329 
330     return (Status == STATUS_PENDING ? FALSE : TRUE);
331 }
332 
333 NTSTATUS NTAPI
334 ConDrvReadConsole(IN PCONSOLE Console,
335                   IN PCONSOLE_INPUT_BUFFER InputBuffer,
336                   IN BOOLEAN Unicode,
337                   OUT PVOID Buffer,
338                   IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
339                   IN PVOID Parameter OPTIONAL,
340                   IN ULONG NumCharsToRead,
341                   OUT PULONG NumCharsRead OPTIONAL);
342 static NTSTATUS
343 ReadChars(IN PGET_INPUT_INFO InputInfo,
344           IN PCSR_API_MESSAGE ApiMessage,
345           IN BOOLEAN CreateWaitBlock OPTIONAL)
346 {
347     NTSTATUS Status;
348     PCONSOLE_READCONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest;
349     PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer;
350     CONSOLE_READCONSOLE_CONTROL ReadControl;
351 
352     UNICODE_STRING ExeName;
353 
354     PVOID Buffer;
355     ULONG NrCharactersRead = 0;
356     ULONG CharSize = (ReadConsoleRequest->Unicode ? sizeof(WCHAR) : sizeof(CHAR));
357 
358     /* Retrieve the executable name, if needed */
359     if (ReadConsoleRequest->InitialNumBytes == 0 &&
360         ReadConsoleRequest->ExeLength <= sizeof(ReadConsoleRequest->StaticBuffer))
361     {
362         ExeName.Length = ExeName.MaximumLength = ReadConsoleRequest->ExeLength;
363         ExeName.Buffer = (PWCHAR)ReadConsoleRequest->StaticBuffer;
364     }
365     else
366     {
367         ExeName.Length = ExeName.MaximumLength = 0;
368         ExeName.Buffer = NULL;
369     }
370 
371     /* Build the ReadControl structure */
372     ReadControl.nLength           = sizeof(CONSOLE_READCONSOLE_CONTROL);
373     ReadControl.nInitialChars     = ReadConsoleRequest->InitialNumBytes / CharSize;
374     ReadControl.dwCtrlWakeupMask  = ReadConsoleRequest->CtrlWakeupMask;
375     ReadControl.dwControlKeyState = ReadConsoleRequest->ControlKeyState;
376 
377     /*
378      * For optimization purposes, Windows (and hence ReactOS, too, for
379      * compatibility reasons) uses a static buffer if no more than eighty
380      * bytes are read. Otherwise a new buffer is used.
381      * The client-side expects that we know this behaviour.
382      */
383     if (ReadConsoleRequest->CaptureBufferSize <= sizeof(ReadConsoleRequest->StaticBuffer))
384     {
385         /*
386          * Adjust the internal pointer, because its old value points to
387          * the static buffer in the original ApiMessage structure.
388          */
389         // ReadConsoleRequest->Buffer = ReadConsoleRequest->StaticBuffer;
390         Buffer = ReadConsoleRequest->StaticBuffer;
391     }
392     else
393     {
394         Buffer = ReadConsoleRequest->Buffer;
395     }
396 
397     DPRINT("Calling ConDrvReadConsole(%wZ)\n", &ExeName);
398     Status = ConDrvReadConsole(InputBuffer->Header.Console,
399                                InputBuffer,
400                                ReadConsoleRequest->Unicode,
401                                Buffer,
402                                &ReadControl,
403                                &ExeName,
404                                ReadConsoleRequest->NumBytes / CharSize, // NrCharactersToRead
405                                &NrCharactersRead);
406     DPRINT("ConDrvReadConsole returned (%d ; Status = 0x%08x)\n",
407            NrCharactersRead, Status);
408 
409     // ReadConsoleRequest->ControlKeyState = ReadControl.dwControlKeyState;
410 
411     if (Status == STATUS_PENDING)
412     {
413         /* We haven't completed a read, so start a wait */
414         return WaitBeforeReading(InputInfo,
415                                  ApiMessage,
416                                  ReadCharsThread,
417                                  CreateWaitBlock);
418     }
419     else
420     {
421         /*
422          * We read all what we wanted. Set the number of bytes read and
423          * return the error code we were given.
424          */
425         ReadConsoleRequest->NumBytes = NrCharactersRead * CharSize;
426         ReadConsoleRequest->ControlKeyState = ReadControl.dwControlKeyState;
427 
428         return Status;
429         // return STATUS_SUCCESS;
430     }
431 }
432 
433 static NTSTATUS
434 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo,
435                 IN PCSR_API_MESSAGE ApiMessage,
436                 IN BOOLEAN CreateWaitBlock OPTIONAL);
437 
438 // Wait function CSR_WAIT_FUNCTION
439 static BOOLEAN
440 NTAPI
441 ReadInputBufferThread(IN PLIST_ENTRY WaitList,
442                       IN PCSR_THREAD WaitThread,
443                       IN PCSR_API_MESSAGE WaitApiMessage,
444                       IN PVOID WaitContext,
445                       IN PVOID WaitArgument1,
446                       IN PVOID WaitArgument2,
447                       IN ULONG WaitFlags)
448 {
449     NTSTATUS Status;
450     PGET_INPUT_INFO InputInfo = (PGET_INPUT_INFO)WaitContext;
451 
452     PVOID InputHandle = WaitArgument2;
453 
454     DPRINT("ReadInputBufferThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext, WaitArgument1, WaitArgument2, WaitFlags);
455 
456     /*
457      * If we are notified of the process termination via a call
458      * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
459      * CsrDestroyThread, just return.
460      */
461     if (WaitFlags & CsrProcessTerminating)
462     {
463         Status = STATUS_THREAD_IS_TERMINATING;
464         goto Quit;
465     }
466 
467     /*
468      * Somebody is closing a handle to this input buffer,
469      * by calling ConSrvCloseHandleEntry.
470      * See whether we are linked to that handle (ie. we
471      * are a waiter for this handle), and if so, return.
472      * Otherwise, ignore the call and continue waiting.
473      */
474     if (InputHandle != NULL)
475     {
476         Status = (InputHandle == InputInfo->HandleEntry ? STATUS_ALERTED
477                                                         : STATUS_PENDING);
478         goto Quit;
479     }
480 
481     /*
482      * If we go there, that means we are notified for some new input.
483      * The console is therefore already locked.
484      */
485     Status = ReadInputBuffer(InputInfo, WaitApiMessage, FALSE);
486 
487 Quit:
488     if (Status != STATUS_PENDING)
489     {
490         WaitApiMessage->Status = Status;
491         ConsoleFreeHeap(InputInfo);
492     }
493 
494     return (Status == STATUS_PENDING ? FALSE : TRUE);
495 }
496 
497 NTSTATUS NTAPI
498 ConDrvGetConsoleInput(IN PCONSOLE Console,
499                       IN PCONSOLE_INPUT_BUFFER InputBuffer,
500                       IN BOOLEAN KeepEvents,
501                       IN BOOLEAN WaitForMoreEvents,
502                       OUT PINPUT_RECORD InputRecord,
503                       IN ULONG NumEventsToRead,
504                       OUT PULONG NumEventsRead OPTIONAL);
505 static NTSTATUS
506 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo,
507                 IN PCSR_API_MESSAGE ApiMessage,
508                 IN BOOLEAN CreateWaitBlock OPTIONAL)
509 {
510     NTSTATUS Status;
511     PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetInputRequest;
512     PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer;
513     ULONG NumEventsRead;
514 
515     PINPUT_RECORD InputRecord;
516 
517     /*
518      * For optimization purposes, Windows (and hence ReactOS, too, for
519      * compatibility reasons) uses a static buffer if no more than five
520      * input records are read. Otherwise a new buffer is used.
521      * The client-side expects that we know this behaviour.
522      */
523     if (GetInputRequest->NumRecords <= sizeof(GetInputRequest->RecordStaticBuffer)/sizeof(INPUT_RECORD))
524     {
525         /*
526          * Adjust the internal pointer, because its old value points to
527          * the static buffer in the original ApiMessage structure.
528          */
529         // GetInputRequest->RecordBufPtr = GetInputRequest->RecordStaticBuffer;
530         InputRecord = GetInputRequest->RecordStaticBuffer;
531     }
532     else
533     {
534         InputRecord = GetInputRequest->RecordBufPtr;
535     }
536 
537     NumEventsRead = 0;
538     Status = ConDrvGetConsoleInput(InputBuffer->Header.Console,
539                                    InputBuffer,
540                                    (GetInputRequest->Flags & CONSOLE_READ_KEEPEVENT) != 0,
541                                    (GetInputRequest->Flags & CONSOLE_READ_CONTINUE ) == 0,
542                                    InputRecord,
543                                    GetInputRequest->NumRecords,
544                                    &NumEventsRead);
545 
546     if (Status == STATUS_PENDING)
547     {
548         /* We haven't completed a read, so start a wait */
549         return WaitBeforeReading(InputInfo,
550                                  ApiMessage,
551                                  ReadInputBufferThread,
552                                  CreateWaitBlock);
553     }
554     else
555     {
556         /*
557          * We read all what we wanted. Set the number of events read and
558          * return the error code we were given.
559          */
560         GetInputRequest->NumRecords = NumEventsRead;
561 
562         if (NT_SUCCESS(Status))
563         {
564             /* Now translate everything to ANSI */
565             if (!GetInputRequest->Unicode)
566             {
567                 ULONG i;
568                 for (i = 0; i < NumEventsRead; ++i)
569                 {
570                     ConioInputEventToAnsi(InputBuffer->Header.Console, &InputRecord[i]);
571                 }
572             }
573         }
574 
575         return Status;
576         // return STATUS_SUCCESS;
577     }
578 }
579 
580 
581 /* PUBLIC SERVER APIS *********************************************************/
582 
583 /* API_NUMBER: ConsolepReadConsole */
584 CON_API(SrvReadConsole,
585         CONSOLE_READCONSOLE, ReadConsoleRequest)
586 {
587     NTSTATUS Status;
588     PVOID HandleEntry;
589     PCONSOLE_INPUT_BUFFER InputBuffer;
590     GET_INPUT_INFO InputInfo;
591 
592     DPRINT("SrvReadConsole\n");
593 
594     /*
595      * For optimization purposes, Windows (and hence ReactOS, too, for
596      * compatibility reasons) uses a static buffer if no more than eighty
597      * bytes are read. Otherwise a new buffer is used.
598      * The client-side expects that we know this behaviour.
599      */
600     if (ReadConsoleRequest->CaptureBufferSize <= sizeof(ReadConsoleRequest->StaticBuffer))
601     {
602         /*
603          * Adjust the internal pointer, because its old value points to
604          * the static buffer in the original ApiMessage structure.
605          */
606         // ReadConsoleRequest->Buffer = ReadConsoleRequest->StaticBuffer;
607     }
608     else
609     {
610         if (!CsrValidateMessageBuffer(ApiMessage,
611                                       (PVOID*)&ReadConsoleRequest->Buffer,
612                                       ReadConsoleRequest->CaptureBufferSize,
613                                       sizeof(BYTE)))
614         {
615             return STATUS_INVALID_PARAMETER;
616         }
617     }
618 
619     if (ReadConsoleRequest->InitialNumBytes > ReadConsoleRequest->NumBytes)
620     {
621         return STATUS_INVALID_PARAMETER;
622     }
623 
624     Status = ConSrvGetInputBufferAndHandleEntry(ProcessData,
625                                                 ReadConsoleRequest->InputHandle,
626                                                 &InputBuffer,
627                                                 &HandleEntry,
628                                                 GENERIC_READ,
629                                                 TRUE);
630     if (!NT_SUCCESS(Status))
631         return Status;
632 
633     ASSERT((PCONSOLE)Console == InputBuffer->Header.Console);
634 
635     InputInfo.CallingThread = CsrGetClientThread();
636     InputInfo.HandleEntry   = HandleEntry;
637     InputInfo.InputBuffer   = InputBuffer;
638 
639     Status = ReadChars(&InputInfo, ApiMessage, TRUE);
640 
641     ConSrvReleaseInputBuffer(InputBuffer, TRUE);
642 
643     if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending;
644 
645     return Status;
646 }
647 
648 /* API_NUMBER: ConsolepGetConsoleInput */
649 CON_API(SrvGetConsoleInput,
650         CONSOLE_GETINPUT, GetInputRequest)
651 {
652     NTSTATUS Status;
653     PVOID HandleEntry;
654     PCONSOLE_INPUT_BUFFER InputBuffer;
655     GET_INPUT_INFO InputInfo;
656 
657     DPRINT("SrvGetConsoleInput\n");
658 
659     if (GetInputRequest->Flags & ~(CONSOLE_READ_KEEPEVENT | CONSOLE_READ_CONTINUE))
660     {
661         return STATUS_INVALID_PARAMETER;
662     }
663 
664     /*
665      * For optimization purposes, Windows (and hence ReactOS, too, for
666      * compatibility reasons) uses a static buffer if no more than five
667      * input records are read. Otherwise a new buffer is used.
668      * The client-side expects that we know this behaviour.
669      */
670     if (GetInputRequest->NumRecords <= sizeof(GetInputRequest->RecordStaticBuffer)/sizeof(INPUT_RECORD))
671     {
672         /*
673          * Adjust the internal pointer, because its old value points to
674          * the static buffer in the original ApiMessage structure.
675          */
676         // GetInputRequest->RecordBufPtr = GetInputRequest->RecordStaticBuffer;
677     }
678     else
679     {
680         if (!CsrValidateMessageBuffer(ApiMessage,
681                                       (PVOID*)&GetInputRequest->RecordBufPtr,
682                                       GetInputRequest->NumRecords,
683                                       sizeof(INPUT_RECORD)))
684         {
685             return STATUS_INVALID_PARAMETER;
686         }
687     }
688 
689     Status = ConSrvGetInputBufferAndHandleEntry(ProcessData,
690                                                 GetInputRequest->InputHandle,
691                                                 &InputBuffer,
692                                                 &HandleEntry,
693                                                 GENERIC_READ,
694                                                 TRUE);
695     if (!NT_SUCCESS(Status))
696         return Status;
697 
698     ASSERT((PCONSOLE)Console == InputBuffer->Header.Console);
699 
700     InputInfo.CallingThread = CsrGetClientThread();
701     InputInfo.HandleEntry   = HandleEntry;
702     InputInfo.InputBuffer   = InputBuffer;
703 
704     Status = ReadInputBuffer(&InputInfo, ApiMessage, TRUE);
705 
706     ConSrvReleaseInputBuffer(InputBuffer, TRUE);
707 
708     if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending;
709 
710     return Status;
711 }
712 
713 #if 0
714 NTSTATUS NTAPI
715 ConDrvWriteConsoleInput(IN PCONSOLE Console,
716                         IN PCONSOLE_INPUT_BUFFER InputBuffer,
717                         IN BOOLEAN AppendToEnd,
718                         IN PINPUT_RECORD InputRecord,
719                         IN ULONG NumEventsToWrite,
720                         OUT PULONG NumEventsWritten OPTIONAL);
721 #endif
722 
723 /* API_NUMBER: ConsolepWriteConsoleInput */
724 CON_API(SrvWriteConsoleInput,
725         CONSOLE_WRITEINPUT, WriteInputRequest)
726 {
727     NTSTATUS Status;
728     PCONSOLE_INPUT_BUFFER InputBuffer;
729     ULONG NumEventsWritten;
730     PINPUT_RECORD InputRecord;
731 
732     /*
733      * For optimization purposes, Windows (and hence ReactOS, too, for
734      * compatibility reasons) uses a static buffer if no more than five
735      * input records are written. Otherwise a new buffer is used.
736      * The client-side expects that we know this behaviour.
737      */
738     if (WriteInputRequest->NumRecords <= sizeof(WriteInputRequest->RecordStaticBuffer)/sizeof(INPUT_RECORD))
739     {
740         /*
741          * Adjust the internal pointer, because its old value points to
742          * the static buffer in the original ApiMessage structure.
743          */
744         // WriteInputRequest->RecordBufPtr = WriteInputRequest->RecordStaticBuffer;
745         InputRecord = WriteInputRequest->RecordStaticBuffer;
746     }
747     else
748     {
749         if (!CsrValidateMessageBuffer(ApiMessage,
750                                       (PVOID*)&WriteInputRequest->RecordBufPtr,
751                                       WriteInputRequest->NumRecords,
752                                       sizeof(INPUT_RECORD)))
753         {
754             return STATUS_INVALID_PARAMETER;
755         }
756 
757         InputRecord = WriteInputRequest->RecordBufPtr;
758     }
759 
760     Status = ConSrvGetInputBuffer(ProcessData,
761                                   WriteInputRequest->InputHandle,
762                                   &InputBuffer, GENERIC_WRITE, TRUE);
763     if (!NT_SUCCESS(Status))
764     {
765         WriteInputRequest->NumRecords = 0;
766         return Status;
767     }
768 
769     ASSERT((PCONSOLE)Console == InputBuffer->Header.Console);
770 
771     /* First translate everything to UNICODE */
772     if (!WriteInputRequest->Unicode)
773     {
774         ULONG i;
775         for (i = 0; i < WriteInputRequest->NumRecords; ++i)
776         {
777             ConioInputEventToUnicode((PCONSOLE)Console, &InputRecord[i]);
778         }
779     }
780 
781     /* Now, add the events */
782     NumEventsWritten = 0;
783     Status = ConioAddInputEvents(Console,
784                                  // InputBuffer,
785                                  InputRecord,
786                                  WriteInputRequest->NumRecords,
787                                  &NumEventsWritten,
788                                  WriteInputRequest->AppendToEnd);
789 
790     // Status = ConDrvWriteConsoleInput((PCONSOLE)Console,
791                                      // InputBuffer,
792                                      // WriteInputRequest->AppendToEnd,
793                                      // InputRecord,
794                                      // WriteInputRequest->NumRecords,
795                                      // &NumEventsWritten);
796 
797     WriteInputRequest->NumRecords = NumEventsWritten;
798 
799     ConSrvReleaseInputBuffer(InputBuffer, TRUE);
800     return Status;
801 }
802 
803 NTSTATUS NTAPI
804 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
805                               IN PCONSOLE_INPUT_BUFFER InputBuffer);
806 /* API_NUMBER: ConsolepFlushInputBuffer */
807 CON_API(SrvFlushConsoleInputBuffer,
808         CONSOLE_FLUSHINPUTBUFFER, FlushInputBufferRequest)
809 {
810     NTSTATUS Status;
811     PCONSOLE_INPUT_BUFFER InputBuffer;
812 
813     Status = ConSrvGetInputBuffer(ProcessData,
814                                   FlushInputBufferRequest->InputHandle,
815                                   &InputBuffer, GENERIC_WRITE, TRUE);
816     if (!NT_SUCCESS(Status))
817         return Status;
818 
819     ASSERT((PCONSOLE)Console == InputBuffer->Header.Console);
820 
821     Status = ConDrvFlushConsoleInputBuffer((PCONSOLE)Console, InputBuffer);
822 
823     ConSrvReleaseInputBuffer(InputBuffer, TRUE);
824     return Status;
825 }
826 
827 NTSTATUS NTAPI
828 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
829                                     IN PCONSOLE_INPUT_BUFFER InputBuffer,
830                                     OUT PULONG NumberOfEvents);
831 /* API_NUMBER: ConsolepGetNumberOfInputEvents */
832 CON_API(SrvGetConsoleNumberOfInputEvents,
833         CONSOLE_GETNUMINPUTEVENTS, GetNumInputEventsRequest)
834 {
835     NTSTATUS Status;
836     PCONSOLE_INPUT_BUFFER InputBuffer;
837 
838     Status = ConSrvGetInputBuffer(ProcessData,
839                                   GetNumInputEventsRequest->InputHandle,
840                                   &InputBuffer, GENERIC_READ, TRUE);
841     if (!NT_SUCCESS(Status))
842         return Status;
843 
844     ASSERT((PCONSOLE)Console == InputBuffer->Header.Console);
845 
846     Status = ConDrvGetConsoleNumberOfInputEvents((PCONSOLE)Console,
847                                                  InputBuffer,
848                                                  &GetNumInputEventsRequest->NumberOfEvents);
849 
850     ConSrvReleaseInputBuffer(InputBuffer, TRUE);
851     return Status;
852 }
853 
854 /* EOF */
855