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
,
138 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl
,
139 IN ULONG NumCharsToRead
,
140 OUT PULONG NumCharsRead OPTIONAL
);
142 ReadChars(IN PGET_INPUT_INFO InputInfo
,
143 IN PCSR_API_MESSAGE ApiMessage
,
144 IN BOOLEAN CreateWaitBlock OPTIONAL
)
147 PCONSOLE_READCONSOLE ReadConsoleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ReadConsoleRequest
;
148 PCONSOLE_INPUT_BUFFER InputBuffer
= InputInfo
->InputBuffer
;
149 CONSOLE_READCONSOLE_CONTROL ReadControl
;
151 ReadControl
.nLength
= sizeof(CONSOLE_READCONSOLE_CONTROL
);
152 ReadControl
.nInitialChars
= ReadConsoleRequest
->NrCharactersRead
;
153 ReadControl
.dwCtrlWakeupMask
= ReadConsoleRequest
->CtrlWakeupMask
;
154 ReadControl
.dwControlKeyState
= ReadConsoleRequest
->ControlKeyState
;
156 Status
= ConDrvReadConsole(InputBuffer
->Header
.Console
,
158 ReadConsoleRequest
->Unicode
,
159 ReadConsoleRequest
->Buffer
,
161 ReadConsoleRequest
->NrCharactersToRead
,
162 &ReadConsoleRequest
->NrCharactersRead
);
164 ReadConsoleRequest
->ControlKeyState
= ReadControl
.dwControlKeyState
;
166 if (Status
== STATUS_PENDING
)
168 /* We haven't completed a read, so start a wait */
169 return WaitBeforeReading(InputInfo
,
176 /* We read all what we wanted, we return the error code we were given */
178 // return STATUS_SUCCESS;
183 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo
,
184 IN PCSR_API_MESSAGE ApiMessage
,
185 IN BOOLEAN CreateWaitBlock OPTIONAL
);
187 // Wait function CSR_WAIT_FUNCTION
190 ReadInputBufferThread(IN PLIST_ENTRY WaitList
,
191 IN PCSR_THREAD WaitThread
,
192 IN PCSR_API_MESSAGE WaitApiMessage
,
193 IN PVOID WaitContext
,
194 IN PVOID WaitArgument1
,
195 IN PVOID WaitArgument2
,
199 PGET_INPUT_INFO InputInfo
= (PGET_INPUT_INFO
)WaitContext
;
201 PVOID InputHandle
= WaitArgument2
;
203 DPRINT("ReadInputBufferThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext
, WaitArgument1
, WaitArgument2
, WaitFlags
);
206 * If we are notified of the process termination via a call
207 * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
208 * CsrDestroyThread, just return.
210 if (WaitFlags
& CsrProcessTerminating
)
212 Status
= STATUS_THREAD_IS_TERMINATING
;
217 * Somebody is closing a handle to this input buffer,
218 * by calling ConSrvCloseHandleEntry.
219 * See whether we are linked to that handle (ie. we
220 * are a waiter for this handle), and if so, return.
221 * Otherwise, ignore the call and continue waiting.
223 if (InputHandle
!= NULL
)
225 Status
= (InputHandle
== InputInfo
->HandleEntry
? STATUS_ALERTED
231 * If we go there, that means we are notified for some new input.
232 * The console is therefore already locked.
234 Status
= ReadInputBuffer(InputInfo
, WaitApiMessage
, FALSE
);
237 if (Status
!= STATUS_PENDING
)
239 WaitApiMessage
->Status
= Status
;
240 ConsoleFreeHeap(InputInfo
);
243 return (Status
== STATUS_PENDING
? FALSE
: TRUE
);
247 ConDrvGetConsoleInput(IN PCONSOLE Console
,
248 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
249 IN BOOLEAN KeepEvents
,
250 IN BOOLEAN WaitForMoreEvents
,
252 OUT PINPUT_RECORD InputRecord
,
253 IN ULONG NumEventsToRead
,
254 OUT PULONG NumEventsRead OPTIONAL
);
256 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo
,
257 IN PCSR_API_MESSAGE ApiMessage
,
258 IN BOOLEAN CreateWaitBlock OPTIONAL
)
261 PCONSOLE_GETINPUT GetInputRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetInputRequest
;
262 PCONSOLE_INPUT_BUFFER InputBuffer
= InputInfo
->InputBuffer
;
265 PINPUT_RECORD InputRecord
;
268 * For optimization purposes, Windows (and hence ReactOS, too, for
269 * compatibility reasons) uses a static buffer if no more than five
270 * input records are read. Otherwise a new buffer is used.
271 * The client-side expects that we know this behaviour.
273 if (GetInputRequest
->NumRecords
<= sizeof(GetInputRequest
->RecordStaticBuffer
)/sizeof(INPUT_RECORD
))
276 * Adjust the internal pointer, because its old value points to
277 * the static buffer in the original ApiMessage structure.
279 // GetInputRequest->RecordBufPtr = GetInputRequest->RecordStaticBuffer;
280 InputRecord
= GetInputRequest
->RecordStaticBuffer
;
284 InputRecord
= GetInputRequest
->RecordBufPtr
;
288 Status
= ConDrvGetConsoleInput(InputBuffer
->Header
.Console
,
290 (GetInputRequest
->Flags
& CONSOLE_READ_KEEPEVENT
) != 0,
291 (GetInputRequest
->Flags
& CONSOLE_READ_CONTINUE
) == 0,
292 GetInputRequest
->Unicode
,
294 GetInputRequest
->NumRecords
,
297 if (Status
== STATUS_PENDING
)
299 /* We haven't completed a read, so start a wait */
300 return WaitBeforeReading(InputInfo
,
302 ReadInputBufferThread
,
307 /* We read all what we wanted, we return the error code we were given */
308 GetInputRequest
->NumRecords
= NumEventsRead
;
310 // return STATUS_SUCCESS;
315 /* PUBLIC SERVER APIS *********************************************************/
317 CSR_API(SrvReadConsole
)
320 PCONSOLE_READCONSOLE ReadConsoleRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ReadConsoleRequest
;
321 PCONSOLE_PROCESS_DATA ProcessData
= ConsoleGetPerProcessData(CsrGetClientThread()->Process
);
323 PCONSOLE_INPUT_BUFFER InputBuffer
;
324 GET_INPUT_INFO InputInfo
;
326 DPRINT("SrvReadConsole\n");
328 if (!CsrValidateMessageBuffer(ApiMessage
,
329 (PVOID
*)&ReadConsoleRequest
->Buffer
,
330 ReadConsoleRequest
->BufferSize
,
333 return STATUS_INVALID_PARAMETER
;
336 if (ReadConsoleRequest
->NrCharactersRead
> ReadConsoleRequest
->NrCharactersToRead
)
338 return STATUS_INVALID_PARAMETER
;
341 Status
= ConSrvGetInputBufferAndHandleEntry(ProcessData
, ReadConsoleRequest
->InputHandle
, &InputBuffer
, &HandleEntry
, GENERIC_READ
, TRUE
);
342 if (!NT_SUCCESS(Status
)) return Status
;
344 // This member is set by the caller (IntReadConsole in kernel32)
345 // ReadConsoleRequest->NrCharactersRead = 0;
347 InputInfo
.CallingThread
= CsrGetClientThread();
348 InputInfo
.HandleEntry
= HandleEntry
;
349 InputInfo
.InputBuffer
= InputBuffer
;
351 Status
= ReadChars(&InputInfo
, ApiMessage
, TRUE
);
353 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);
355 if (Status
== STATUS_PENDING
) *ReplyCode
= CsrReplyPending
;
360 CSR_API(SrvGetConsoleInput
)
363 PCONSOLE_GETINPUT GetInputRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetInputRequest
;
364 PCONSOLE_PROCESS_DATA ProcessData
= ConsoleGetPerProcessData(CsrGetClientThread()->Process
);
366 PCONSOLE_INPUT_BUFFER InputBuffer
;
367 GET_INPUT_INFO InputInfo
;
369 DPRINT("SrvGetConsoleInput\n");
371 if (GetInputRequest
->Flags
& ~(CONSOLE_READ_KEEPEVENT
| CONSOLE_READ_CONTINUE
))
372 return STATUS_INVALID_PARAMETER
;
375 * For optimization purposes, Windows (and hence ReactOS, too, for
376 * compatibility reasons) uses a static buffer if no more than five
377 * input records are read. Otherwise a new buffer is used.
378 * The client-side expects that we know this behaviour.
380 if (GetInputRequest
->NumRecords
<= sizeof(GetInputRequest
->RecordStaticBuffer
)/sizeof(INPUT_RECORD
))
383 * Adjust the internal pointer, because its old value points to
384 * the static buffer in the original ApiMessage structure.
386 // GetInputRequest->RecordBufPtr = GetInputRequest->RecordStaticBuffer;
390 if (!CsrValidateMessageBuffer(ApiMessage
,
391 (PVOID
*)&GetInputRequest
->RecordBufPtr
,
392 GetInputRequest
->NumRecords
,
393 sizeof(INPUT_RECORD
)))
395 return STATUS_INVALID_PARAMETER
;
399 Status
= ConSrvGetInputBufferAndHandleEntry(ProcessData
, GetInputRequest
->InputHandle
, &InputBuffer
, &HandleEntry
, GENERIC_READ
, TRUE
);
400 if (!NT_SUCCESS(Status
)) return Status
;
402 InputInfo
.CallingThread
= CsrGetClientThread();
403 InputInfo
.HandleEntry
= HandleEntry
;
404 InputInfo
.InputBuffer
= InputBuffer
;
406 Status
= ReadInputBuffer(&InputInfo
, ApiMessage
, TRUE
);
408 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);
410 if (Status
== STATUS_PENDING
) *ReplyCode
= CsrReplyPending
;
416 ConDrvWriteConsoleInput(IN PCONSOLE Console
,
417 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
419 IN BOOLEAN AppendToEnd
,
420 IN PINPUT_RECORD InputRecord
,
421 IN ULONG NumEventsToWrite
,
422 OUT PULONG NumEventsWritten OPTIONAL
);
423 CSR_API(SrvWriteConsoleInput
)
426 PCONSOLE_WRITEINPUT WriteInputRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.WriteInputRequest
;
427 PCONSOLE_INPUT_BUFFER InputBuffer
;
428 ULONG NumEventsWritten
;
430 PINPUT_RECORD InputRecord
;
432 DPRINT("SrvWriteConsoleInput\n");
435 * For optimization purposes, Windows (and hence ReactOS, too, for
436 * compatibility reasons) uses a static buffer if no more than five
437 * input records are written. Otherwise a new buffer is used.
438 * The client-side expects that we know this behaviour.
440 if (WriteInputRequest
->NumRecords
<= sizeof(WriteInputRequest
->RecordStaticBuffer
)/sizeof(INPUT_RECORD
))
443 * Adjust the internal pointer, because its old value points to
444 * the static buffer in the original ApiMessage structure.
446 // WriteInputRequest->RecordBufPtr = WriteInputRequest->RecordStaticBuffer;
447 InputRecord
= WriteInputRequest
->RecordStaticBuffer
;
451 if (!CsrValidateMessageBuffer(ApiMessage
,
452 (PVOID
*)&WriteInputRequest
->RecordBufPtr
,
453 WriteInputRequest
->NumRecords
,
454 sizeof(INPUT_RECORD
)))
456 return STATUS_INVALID_PARAMETER
;
459 InputRecord
= WriteInputRequest
->RecordBufPtr
;
462 Status
= ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
463 WriteInputRequest
->InputHandle
,
464 &InputBuffer
, GENERIC_WRITE
, TRUE
);
465 if (!NT_SUCCESS(Status
)) return Status
;
467 NumEventsWritten
= 0;
468 Status
= ConDrvWriteConsoleInput(InputBuffer
->Header
.Console
,
470 WriteInputRequest
->Unicode
,
471 WriteInputRequest
->AppendToEnd
,
473 WriteInputRequest
->NumRecords
,
475 WriteInputRequest
->NumRecords
= NumEventsWritten
;
477 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);
482 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console
,
483 IN PCONSOLE_INPUT_BUFFER InputBuffer
);
484 CSR_API(SrvFlushConsoleInputBuffer
)
487 PCONSOLE_FLUSHINPUTBUFFER FlushInputBufferRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.FlushInputBufferRequest
;
488 PCONSOLE_INPUT_BUFFER InputBuffer
;
490 DPRINT("SrvFlushConsoleInputBuffer\n");
492 Status
= ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
493 FlushInputBufferRequest
->InputHandle
,
494 &InputBuffer
, GENERIC_WRITE
, TRUE
);
495 if (!NT_SUCCESS(Status
)) return Status
;
497 Status
= ConDrvFlushConsoleInputBuffer(InputBuffer
->Header
.Console
,
500 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);
505 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console
,
506 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
507 OUT PULONG NumberOfEvents
);
508 CSR_API(SrvGetConsoleNumberOfInputEvents
)
511 PCONSOLE_GETNUMINPUTEVENTS GetNumInputEventsRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetNumInputEventsRequest
;
512 PCONSOLE_INPUT_BUFFER InputBuffer
;
514 DPRINT("SrvGetConsoleNumberOfInputEvents\n");
516 Status
= ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process
),
517 GetNumInputEventsRequest
->InputHandle
,
518 &InputBuffer
, GENERIC_READ
, TRUE
);
519 if (!NT_SUCCESS(Status
)) return Status
;
521 Status
= ConDrvGetConsoleNumberOfInputEvents(InputBuffer
->Header
.Console
,
523 &GetNumInputEventsRequest
->NumberOfEvents
);
525 ConSrvReleaseInputBuffer(InputBuffer
, TRUE
);