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)
10 /* INCLUDES *******************************************************************/
17 /* GLOBALS ********************************************************************/
19 #define ConSrvGetInputBuffer(ProcessData, Handle, Ptr, Access, LockConsole) \
20 ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), NULL, \
21 (Access), (LockConsole), INPUT_BUFFER)
22 #define ConSrvGetInputBufferAndHandleEntry(ProcessData, Handle, Ptr, Entry, Access, LockConsole) \
23 ConSrvGetObject((ProcessData), (Handle), (PCONSOLE_IO_OBJECT*)(Ptr), (Entry), \
24 (Access), (LockConsole), INPUT_BUFFER)
25 #define ConSrvReleaseInputBuffer(Buff, IsConsoleLocked) \
26 ConSrvReleaseObject(&(Buff)->Header, (IsConsoleLocked))
29 typedef struct _GET_INPUT_INFO
31 PCSR_THREAD CallingThread
; // The thread which called the input API.
32 PVOID HandleEntry
; // The handle data associated with the wait thread.
33 PCONSOLE_INPUT_BUFFER InputBuffer
; // The input buffer corresponding to the handle.
34 } GET_INPUT_INFO
, *PGET_INPUT_INFO
;
37 /* PRIVATE FUNCTIONS **********************************************************/
40 WaitBeforeReading(IN PGET_INPUT_INFO InputInfo
,
41 IN PCSR_API_MESSAGE ApiMessage
,
42 IN CSR_WAIT_FUNCTION WaitFunction OPTIONAL
,
43 IN BOOLEAN CreateWaitBlock OPTIONAL
)
47 PGET_INPUT_INFO CapturedInputInfo
;
49 CapturedInputInfo
= ConsoleAllocHeap(0, sizeof(GET_INPUT_INFO
));
50 if (!CapturedInputInfo
) return STATUS_NO_MEMORY
;
52 RtlMoveMemory(CapturedInputInfo
, InputInfo
, sizeof(GET_INPUT_INFO
));
54 if (!CsrCreateWait(&InputInfo
->InputBuffer
->Header
.Console
->ReadWaitQueue
,
56 InputInfo
->CallingThread
,
60 ConsoleFreeHeap(CapturedInputInfo
);
61 return STATUS_NO_MEMORY
;
66 return STATUS_PENDING
;
70 ReadChars(IN PGET_INPUT_INFO InputInfo
,
71 IN PCSR_API_MESSAGE ApiMessage
,
72 IN BOOLEAN CreateWaitBlock OPTIONAL
);
74 // Wait function CSR_WAIT_FUNCTION
77 ReadCharsThread(IN PLIST_ENTRY WaitList
,
78 IN PCSR_THREAD WaitThread
,
79 IN PCSR_API_MESSAGE WaitApiMessage
,
81 IN PVOID WaitArgument1
,
82 IN PVOID WaitArgument2
,
86 PGET_INPUT_INFO InputInfo
= (PGET_INPUT_INFO
)WaitContext
;
88 PVOID InputHandle
= WaitArgument2
;
90 DPRINT("ReadCharsThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext
, WaitArgument1
, WaitArgument2
, WaitFlags
);
93 * If we are notified of the process termination via a call
94 * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
95 * CsrDestroyThread, just return.
97 if (WaitFlags
& CsrProcessTerminating
)
99 Status
= STATUS_THREAD_IS_TERMINATING
;
104 * Somebody is closing a handle to this input buffer,
105 * by calling ConSrvCloseHandleEntry.
106 * See whether we are linked to that handle (ie. we
107 * are a waiter for this handle), and if so, return.
108 * Otherwise, ignore the call and continue waiting.
110 if (InputHandle
!= NULL
)
112 Status
= (InputHandle
== InputInfo
->HandleEntry
? STATUS_ALERTED
118 * If we go there, that means we are notified for some new input.
119 * The console is therefore already locked.
121 Status
= ReadChars(InputInfo
, WaitApiMessage
, FALSE
);
124 if (Status
!= STATUS_PENDING
)
126 WaitApiMessage
->Status
= Status
;
127 ConsoleFreeHeap(InputInfo
);
130 return (Status
== STATUS_PENDING
? FALSE
: TRUE
);
134 ConDrvReadConsole(IN PCONSOLE Console
,
135 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
136 /**/IN PUNICODE_STRING ExeName
/**/OPTIONAL
/**/,/**/
139 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl
,
140 IN ULONG NumCharsToRead
,
141 OUT PULONG NumCharsRead OPTIONAL
);
143 ReadChars(IN PGET_INPUT_INFO InputInfo
,
144 IN PCSR_API_MESSAGE ApiMessage
,
145 IN BOOLEAN CreateWaitBlock OPTIONAL
)
148 PCONSOLE_READCONSOLE ReadConsoleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ReadConsoleRequest
;
149 PCONSOLE_INPUT_BUFFER InputBuffer
= InputInfo
->InputBuffer
;
150 CONSOLE_READCONSOLE_CONTROL ReadControl
;
152 UNICODE_STRING ExeName
;
155 ULONG NrCharactersRead
= 0;
156 ULONG CharSize
= (ReadConsoleRequest
->Unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
158 /* Compute the executable name, if needed */
159 if (ReadConsoleRequest
->InitialNumBytes
== 0 &&
160 ReadConsoleRequest
->ExeLength
<= sizeof(ReadConsoleRequest
->StaticBuffer
))
162 ExeName
.Length
= ExeName
.MaximumLength
= ReadConsoleRequest
->ExeLength
;
163 ExeName
.Buffer
= (PWCHAR
)ReadConsoleRequest
->StaticBuffer
;
167 ExeName
.Length
= ExeName
.MaximumLength
= 0;
168 ExeName
.Buffer
= NULL
;
171 /* Build the ReadControl structure */
172 ReadControl
.nLength
= sizeof(CONSOLE_READCONSOLE_CONTROL
);
173 ReadControl
.nInitialChars
= ReadConsoleRequest
->InitialNumBytes
/ CharSize
;
174 ReadControl
.dwCtrlWakeupMask
= ReadConsoleRequest
->CtrlWakeupMask
;
175 ReadControl
.dwControlKeyState
= ReadConsoleRequest
->ControlKeyState
;
178 * For optimization purposes, Windows (and hence ReactOS, too, for
179 * compatibility reasons) uses a static buffer if no more than eighty
180 * bytes are read. Otherwise a new buffer is used.
181 * The client-side expects that we know this behaviour.
183 if (ReadConsoleRequest
->CaptureBufferSize
<= sizeof(ReadConsoleRequest
->StaticBuffer
))
186 * Adjust the internal pointer, because its old value points to
187 * the static buffer in the original ApiMessage structure.
189 // ReadConsoleRequest->Buffer = ReadConsoleRequest->StaticBuffer;
190 Buffer
= ReadConsoleRequest
->StaticBuffer
;
194 Buffer
= ReadConsoleRequest
->Buffer
;
197 DPRINT1("Calling ConDrvReadConsole(%wZ)\n", &ExeName
);
198 Status
= ConDrvReadConsole(InputBuffer
->Header
.Console
,
201 ReadConsoleRequest
->Unicode
,
204 ReadConsoleRequest
->NumBytes
/ CharSize
, // NrCharactersToRead
206 DPRINT1("ConDrvReadConsole returned (%d ; Status = 0x%08x)\n",
207 NrCharactersRead
, Status
);
209 // ReadConsoleRequest->ControlKeyState = ReadControl.dwControlKeyState;
211 if (Status
== STATUS_PENDING
)
213 /* We haven't completed a read, so start a wait */
214 return WaitBeforeReading(InputInfo
,
222 * We read all what we wanted. Set the number of bytes read and
223 * return the error code we were given.
225 ReadConsoleRequest
->NumBytes
= NrCharactersRead
* CharSize
;
226 ReadConsoleRequest
->ControlKeyState
= ReadControl
.dwControlKeyState
;
229 // return STATUS_SUCCESS;
234 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo
,
235 IN PCSR_API_MESSAGE ApiMessage
,
236 IN BOOLEAN CreateWaitBlock OPTIONAL
);
238 // Wait function CSR_WAIT_FUNCTION
241 ReadInputBufferThread(IN PLIST_ENTRY WaitList
,
242 IN PCSR_THREAD WaitThread
,
243 IN PCSR_API_MESSAGE WaitApiMessage
,
244 IN PVOID WaitContext
,
245 IN PVOID WaitArgument1
,
246 IN PVOID WaitArgument2
,
250 PGET_INPUT_INFO InputInfo
= (PGET_INPUT_INFO
)WaitContext
;
252 PVOID InputHandle
= WaitArgument2
;
254 DPRINT("ReadInputBufferThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext
, WaitArgument1
, WaitArgument2
, WaitFlags
);
257 * If we are notified of the process termination via a call
258 * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
259 * CsrDestroyThread, just return.
261 if (WaitFlags
& CsrProcessTerminating
)
263 Status
= STATUS_THREAD_IS_TERMINATING
;
268 * Somebody is closing a handle to this input buffer,
269 * by calling ConSrvCloseHandleEntry.
270 * See whether we are linked to that handle (ie. we
271 * are a waiter for this handle), and if so, return.
272 * Otherwise, ignore the call and continue waiting.
274 if (InputHandle
!= NULL
)
276 Status
= (InputHandle
== InputInfo
->HandleEntry
? STATUS_ALERTED
282 * If we go there, that means we are notified for some new input.
283 * The console is therefore already locked.
285 Status
= ReadInputBuffer(InputInfo
, WaitApiMessage
, FALSE
);
288 if (Status
!= STATUS_PENDING
)
290 WaitApiMessage
->Status
= Status
;
291 ConsoleFreeHeap(InputInfo
);
294 return (Status
== STATUS_PENDING
? FALSE
: TRUE
);
298 ConDrvGetConsoleInput(IN PCONSOLE Console
,
299 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
300 IN BOOLEAN KeepEvents
,
301 IN BOOLEAN WaitForMoreEvents
,
303 OUT PINPUT_RECORD InputRecord
,
304 IN ULONG NumEventsToRead
,
305 OUT PULONG NumEventsRead OPTIONAL
);
307 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo
,
308 IN PCSR_API_MESSAGE ApiMessage
,
309 IN BOOLEAN CreateWaitBlock OPTIONAL
)
312 PCONSOLE_GETINPUT GetInputRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetInputRequest
;
313 PCONSOLE_INPUT_BUFFER InputBuffer
= InputInfo
->InputBuffer
;
316 PINPUT_RECORD InputRecord
;
319 * For optimization purposes, Windows (and hence ReactOS, too, for
320 * compatibility reasons) uses a static buffer if no more than five
321 * input records are read. Otherwise a new buffer is used.
322 * The client-side expects that we know this behaviour.
324 if (GetInputRequest
->NumRecords
<= sizeof(GetInputRequest
->RecordStaticBuffer
)/sizeof(INPUT_RECORD
))
327 * Adjust the internal pointer, because its old value points to
328 * the static buffer in the original ApiMessage structure.
330 // GetInputRequest->RecordBufPtr = GetInputRequest->RecordStaticBuffer;
331 InputRecord
= GetInputRequest
->RecordStaticBuffer
;
335 InputRecord
= GetInputRequest
->RecordBufPtr
;
339 Status
= ConDrvGetConsoleInput(InputBuffer
->Header
.Console
,
341 (GetInputRequest
->Flags
& CONSOLE_READ_KEEPEVENT
) != 0,
342 (GetInputRequest
->Flags
& CONSOLE_READ_CONTINUE
) == 0,
343 GetInputRequest
->Unicode
,
345 GetInputRequest
->NumRecords
,
348 if (Status
== STATUS_PENDING
)
350 /* We haven't completed a read, so start a wait */
351 return WaitBeforeReading(InputInfo
,
353 ReadInputBufferThread
,
359 * We read all what we wanted. Set the number of events read and
360 * return the error code we were given.
362 GetInputRequest
->NumRecords
= NumEventsRead
;
365 // return STATUS_SUCCESS;
370 /* PUBLIC SERVER APIS *********************************************************/
372 CSR_API(SrvReadConsole
)
375 PCONSOLE_READCONSOLE ReadConsoleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ReadConsoleRequest
;
376 PCONSOLE_PROCESS_DATA ProcessData
= ConsoleGetPerProcessData(CsrGetClientThread()->Process
);
378 PCONSOLE_INPUT_BUFFER InputBuffer
;
379 GET_INPUT_INFO InputInfo
;
381 DPRINT("SrvReadConsole\n");
384 * For optimization purposes, Windows (and hence ReactOS, too, for
385 * compatibility reasons) uses a static buffer if no more than eighty
386 * bytes are read. Otherwise a new buffer is used.
387 * The client-side expects that we know this behaviour.
389 if (ReadConsoleRequest
->CaptureBufferSize
<= sizeof(ReadConsoleRequest
->StaticBuffer
))
392 * Adjust the internal pointer, because its old value points to
393 * the static buffer in the original ApiMessage structure.
395 // ReadConsoleRequest->Buffer = ReadConsoleRequest->StaticBuffer;
399 if (!CsrValidateMessageBuffer(ApiMessage
,
400 (PVOID
*)&ReadConsoleRequest
->Buffer
,
401 ReadConsoleRequest
->CaptureBufferSize
,
404 return STATUS_INVALID_PARAMETER
;
408 if (ReadConsoleRequest
->InitialNumBytes
> ReadConsoleRequest
->NumBytes
)
410 return STATUS_INVALID_PARAMETER
;
413 Status
= ConSrvGetInputBufferAndHandleEntry(ProcessData
, ReadConsoleRequest
->InputHandle
, &InputBuffer
, &HandleEntry
, GENERIC_READ
, TRUE
);
414 if (!NT_SUCCESS(Status
)) return Status
;
416 InputInfo
.CallingThread
= CsrGetClientThread();
417 InputInfo
.HandleEntry
= HandleEntry
;
418 InputInfo
.InputBuffer
= InputBuffer
;
420 Status
= ReadChars(&InputInfo
, ApiMessage
, TRUE
);
422 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);
424 if (Status
== STATUS_PENDING
) *ReplyCode
= CsrReplyPending
;
429 CSR_API(SrvGetConsoleInput
)
432 PCONSOLE_GETINPUT GetInputRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetInputRequest
;
433 PCONSOLE_PROCESS_DATA ProcessData
= ConsoleGetPerProcessData(CsrGetClientThread()->Process
);
435 PCONSOLE_INPUT_BUFFER InputBuffer
;
436 GET_INPUT_INFO InputInfo
;
438 DPRINT("SrvGetConsoleInput\n");
440 if (GetInputRequest
->Flags
& ~(CONSOLE_READ_KEEPEVENT
| CONSOLE_READ_CONTINUE
))
441 return STATUS_INVALID_PARAMETER
;
444 * For optimization purposes, Windows (and hence ReactOS, too, for
445 * compatibility reasons) uses a static buffer if no more than five
446 * input records are read. Otherwise a new buffer is used.
447 * The client-side expects that we know this behaviour.
449 if (GetInputRequest
->NumRecords
<= sizeof(GetInputRequest
->RecordStaticBuffer
)/sizeof(INPUT_RECORD
))
452 * Adjust the internal pointer, because its old value points to
453 * the static buffer in the original ApiMessage structure.
455 // GetInputRequest->RecordBufPtr = GetInputRequest->RecordStaticBuffer;
459 if (!CsrValidateMessageBuffer(ApiMessage
,
460 (PVOID
*)&GetInputRequest
->RecordBufPtr
,
461 GetInputRequest
->NumRecords
,
462 sizeof(INPUT_RECORD
)))
464 return STATUS_INVALID_PARAMETER
;
468 Status
= ConSrvGetInputBufferAndHandleEntry(ProcessData
, GetInputRequest
->InputHandle
, &InputBuffer
, &HandleEntry
, GENERIC_READ
, TRUE
);
469 if (!NT_SUCCESS(Status
)) return Status
;
471 InputInfo
.CallingThread
= CsrGetClientThread();
472 InputInfo
.HandleEntry
= HandleEntry
;
473 InputInfo
.InputBuffer
= InputBuffer
;
475 Status
= ReadInputBuffer(&InputInfo
, ApiMessage
, TRUE
);
477 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);
479 if (Status
== STATUS_PENDING
) *ReplyCode
= CsrReplyPending
;
485 ConDrvWriteConsoleInput(IN PCONSOLE Console
,
486 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
488 IN BOOLEAN AppendToEnd
,
489 IN PINPUT_RECORD InputRecord
,
490 IN ULONG NumEventsToWrite
,
491 OUT PULONG NumEventsWritten OPTIONAL
);
492 CSR_API(SrvWriteConsoleInput
)
495 PCONSOLE_WRITEINPUT WriteInputRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.WriteInputRequest
;
496 PCONSOLE_INPUT_BUFFER InputBuffer
;
497 ULONG NumEventsWritten
;
499 PINPUT_RECORD InputRecord
;
501 DPRINT("SrvWriteConsoleInput\n");
504 * For optimization purposes, Windows (and hence ReactOS, too, for
505 * compatibility reasons) uses a static buffer if no more than five
506 * input records are written. Otherwise a new buffer is used.
507 * The client-side expects that we know this behaviour.
509 if (WriteInputRequest
->NumRecords
<= sizeof(WriteInputRequest
->RecordStaticBuffer
)/sizeof(INPUT_RECORD
))
512 * Adjust the internal pointer, because its old value points to
513 * the static buffer in the original ApiMessage structure.
515 // WriteInputRequest->RecordBufPtr = WriteInputRequest->RecordStaticBuffer;
516 InputRecord
= WriteInputRequest
->RecordStaticBuffer
;
520 if (!CsrValidateMessageBuffer(ApiMessage
,
521 (PVOID
*)&WriteInputRequest
->RecordBufPtr
,
522 WriteInputRequest
->NumRecords
,
523 sizeof(INPUT_RECORD
)))
525 return STATUS_INVALID_PARAMETER
;
528 InputRecord
= WriteInputRequest
->RecordBufPtr
;
531 Status
= ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
532 WriteInputRequest
->InputHandle
,
533 &InputBuffer
, GENERIC_WRITE
, TRUE
);
534 if (!NT_SUCCESS(Status
))
536 WriteInputRequest
->NumRecords
= 0;
540 NumEventsWritten
= 0;
541 Status
= ConDrvWriteConsoleInput(InputBuffer
->Header
.Console
,
543 WriteInputRequest
->Unicode
,
544 WriteInputRequest
->AppendToEnd
,
546 WriteInputRequest
->NumRecords
,
548 WriteInputRequest
->NumRecords
= NumEventsWritten
;
550 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);
555 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console
,
556 IN PCONSOLE_INPUT_BUFFER InputBuffer
);
557 CSR_API(SrvFlushConsoleInputBuffer
)
560 PCONSOLE_FLUSHINPUTBUFFER FlushInputBufferRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.FlushInputBufferRequest
;
561 PCONSOLE_INPUT_BUFFER InputBuffer
;
563 DPRINT("SrvFlushConsoleInputBuffer\n");
565 Status
= ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
566 FlushInputBufferRequest
->InputHandle
,
567 &InputBuffer
, GENERIC_WRITE
, TRUE
);
568 if (!NT_SUCCESS(Status
)) return Status
;
570 Status
= ConDrvFlushConsoleInputBuffer(InputBuffer
->Header
.Console
,
573 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);
578 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console
,
579 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
580 OUT PULONG NumberOfEvents
);
581 CSR_API(SrvGetConsoleNumberOfInputEvents
)
584 PCONSOLE_GETNUMINPUTEVENTS GetNumInputEventsRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetNumInputEventsRequest
;
585 PCONSOLE_INPUT_BUFFER InputBuffer
;
587 DPRINT("SrvGetConsoleNumberOfInputEvents\n");
589 Status
= ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
590 GetNumInputEventsRequest
->InputHandle
,
591 &InputBuffer
, GENERIC_READ
, TRUE
);
592 if (!NT_SUCCESS(Status
)) return Status
;
594 Status
= ConDrvGetConsoleNumberOfInputEvents(InputBuffer
->Header
.Console
,
596 &GetNumInputEventsRequest
->NumberOfEvents
);
598 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);