5bcd8f627c158560d8682f0734808b2cae014cd0
[reactos.git] / win32ss / user / winsrv / consrv / coninput.c
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 #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))
27
28
29 typedef struct _GET_INPUT_INFO
30 {
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;
35
36
37 /* PRIVATE FUNCTIONS **********************************************************/
38
39 static NTSTATUS
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)
44 {
45 if (CreateWaitBlock)
46 {
47 PGET_INPUT_INFO CapturedInputInfo;
48
49 CapturedInputInfo = ConsoleAllocHeap(0, sizeof(GET_INPUT_INFO));
50 if (!CapturedInputInfo) return STATUS_NO_MEMORY;
51
52 RtlMoveMemory(CapturedInputInfo, InputInfo, sizeof(GET_INPUT_INFO));
53
54 if (!CsrCreateWait(&InputInfo->InputBuffer->Header.Console->ReadWaitQueue,
55 WaitFunction,
56 InputInfo->CallingThread,
57 ApiMessage,
58 CapturedInputInfo))
59 {
60 ConsoleFreeHeap(CapturedInputInfo);
61 return STATUS_NO_MEMORY;
62 }
63 }
64
65 /* Wait for input */
66 return STATUS_PENDING;
67 }
68
69 static NTSTATUS
70 ReadChars(IN PGET_INPUT_INFO InputInfo,
71 IN PCSR_API_MESSAGE ApiMessage,
72 IN BOOLEAN CreateWaitBlock OPTIONAL);
73
74 // Wait function CSR_WAIT_FUNCTION
75 static BOOLEAN
76 NTAPI
77 ReadCharsThread(IN PLIST_ENTRY WaitList,
78 IN PCSR_THREAD WaitThread,
79 IN PCSR_API_MESSAGE WaitApiMessage,
80 IN PVOID WaitContext,
81 IN PVOID WaitArgument1,
82 IN PVOID WaitArgument2,
83 IN ULONG WaitFlags)
84 {
85 NTSTATUS Status;
86 PGET_INPUT_INFO InputInfo = (PGET_INPUT_INFO)WaitContext;
87
88 PVOID InputHandle = WaitArgument2;
89
90 DPRINT("ReadCharsThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext, WaitArgument1, WaitArgument2, WaitFlags);
91
92 /*
93 * If we are notified of the process termination via a call
94 * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
95 * CsrDestroyThread, just return.
96 */
97 if (WaitFlags & CsrProcessTerminating)
98 {
99 Status = STATUS_THREAD_IS_TERMINATING;
100 goto Quit;
101 }
102
103 /*
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.
109 */
110 if (InputHandle != NULL)
111 {
112 Status = (InputHandle == InputInfo->HandleEntry ? STATUS_ALERTED
113 : STATUS_PENDING);
114 goto Quit;
115 }
116
117 /*
118 * If we go there, that means we are notified for some new input.
119 * The console is therefore already locked.
120 */
121 Status = ReadChars(InputInfo, WaitApiMessage, FALSE);
122
123 Quit:
124 if (Status != STATUS_PENDING)
125 {
126 WaitApiMessage->Status = Status;
127 ConsoleFreeHeap(InputInfo);
128 }
129
130 return (Status == STATUS_PENDING ? FALSE : TRUE);
131 }
132
133 NTSTATUS NTAPI
134 ConDrvReadConsole(IN PCONSOLE Console,
135 IN PCONSOLE_INPUT_BUFFER InputBuffer,
136 IN BOOLEAN Unicode,
137 OUT PVOID Buffer,
138 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
139 IN ULONG NumCharsToRead,
140 OUT PULONG NumCharsRead OPTIONAL);
141 static NTSTATUS
142 ReadChars(IN PGET_INPUT_INFO InputInfo,
143 IN PCSR_API_MESSAGE ApiMessage,
144 IN BOOLEAN CreateWaitBlock OPTIONAL)
145 {
146 NTSTATUS Status;
147 PCONSOLE_READCONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest;
148 PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer;
149 CONSOLE_READCONSOLE_CONTROL ReadControl;
150
151 ReadControl.nLength = sizeof(CONSOLE_READCONSOLE_CONTROL);
152 ReadControl.nInitialChars = ReadConsoleRequest->NrCharactersRead;
153 ReadControl.dwCtrlWakeupMask = ReadConsoleRequest->CtrlWakeupMask;
154 ReadControl.dwControlKeyState = ReadConsoleRequest->ControlKeyState;
155
156 Status = ConDrvReadConsole(InputBuffer->Header.Console,
157 InputBuffer,
158 ReadConsoleRequest->Unicode,
159 ReadConsoleRequest->Buffer,
160 &ReadControl,
161 ReadConsoleRequest->NrCharactersToRead,
162 &ReadConsoleRequest->NrCharactersRead);
163
164 ReadConsoleRequest->ControlKeyState = ReadControl.dwControlKeyState;
165
166 if (Status == STATUS_PENDING)
167 {
168 /* We haven't completed a read, so start a wait */
169 return WaitBeforeReading(InputInfo,
170 ApiMessage,
171 ReadCharsThread,
172 CreateWaitBlock);
173 }
174 else
175 {
176 /* We read all what we wanted, we return the error code we were given */
177 return Status;
178 // return STATUS_SUCCESS;
179 }
180 }
181
182 static NTSTATUS
183 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo,
184 IN PCSR_API_MESSAGE ApiMessage,
185 IN BOOLEAN CreateWaitBlock OPTIONAL);
186
187 // Wait function CSR_WAIT_FUNCTION
188 static BOOLEAN
189 NTAPI
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,
196 IN ULONG WaitFlags)
197 {
198 NTSTATUS Status;
199 PGET_INPUT_INFO InputInfo = (PGET_INPUT_INFO)WaitContext;
200
201 PVOID InputHandle = WaitArgument2;
202
203 DPRINT("ReadInputBufferThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext, WaitArgument1, WaitArgument2, WaitFlags);
204
205 /*
206 * If we are notified of the process termination via a call
207 * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
208 * CsrDestroyThread, just return.
209 */
210 if (WaitFlags & CsrProcessTerminating)
211 {
212 Status = STATUS_THREAD_IS_TERMINATING;
213 goto Quit;
214 }
215
216 /*
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.
222 */
223 if (InputHandle != NULL)
224 {
225 Status = (InputHandle == InputInfo->HandleEntry ? STATUS_ALERTED
226 : STATUS_PENDING);
227 goto Quit;
228 }
229
230 /*
231 * If we go there, that means we are notified for some new input.
232 * The console is therefore already locked.
233 */
234 Status = ReadInputBuffer(InputInfo, WaitApiMessage, FALSE);
235
236 Quit:
237 if (Status != STATUS_PENDING)
238 {
239 WaitApiMessage->Status = Status;
240 ConsoleFreeHeap(InputInfo);
241 }
242
243 return (Status == STATUS_PENDING ? FALSE : TRUE);
244 }
245
246 NTSTATUS NTAPI
247 ConDrvGetConsoleInput(IN PCONSOLE Console,
248 IN PCONSOLE_INPUT_BUFFER InputBuffer,
249 IN BOOLEAN KeepEvents,
250 IN BOOLEAN WaitForMoreEvents,
251 IN BOOLEAN Unicode,
252 OUT PINPUT_RECORD InputRecord,
253 IN ULONG NumEventsToRead,
254 OUT PULONG NumEventsRead OPTIONAL);
255 static NTSTATUS
256 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo,
257 IN PCSR_API_MESSAGE ApiMessage,
258 IN BOOLEAN CreateWaitBlock OPTIONAL)
259 {
260 NTSTATUS Status;
261 PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetInputRequest;
262 PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer;
263 ULONG NumEventsRead;
264
265 PINPUT_RECORD InputRecord;
266
267 /*
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.
272 */
273 if (GetInputRequest->NumRecords <= sizeof(GetInputRequest->RecordStaticBuffer)/sizeof(INPUT_RECORD))
274 {
275 /*
276 * Adjust the internal pointer, because its old value points to
277 * the static buffer in the original ApiMessage structure.
278 */
279 // GetInputRequest->RecordBufPtr = GetInputRequest->RecordStaticBuffer;
280 InputRecord = GetInputRequest->RecordStaticBuffer;
281 }
282 else
283 {
284 InputRecord = GetInputRequest->RecordBufPtr;
285 }
286
287 NumEventsRead = 0;
288 Status = ConDrvGetConsoleInput(InputBuffer->Header.Console,
289 InputBuffer,
290 (GetInputRequest->Flags & CONSOLE_READ_KEEPEVENT) != 0,
291 (GetInputRequest->Flags & CONSOLE_READ_CONTINUE ) == 0,
292 GetInputRequest->Unicode,
293 InputRecord,
294 GetInputRequest->NumRecords,
295 &NumEventsRead);
296
297 if (Status == STATUS_PENDING)
298 {
299 /* We haven't completed a read, so start a wait */
300 return WaitBeforeReading(InputInfo,
301 ApiMessage,
302 ReadInputBufferThread,
303 CreateWaitBlock);
304 }
305 else
306 {
307 /* We read all what we wanted, we return the error code we were given */
308 GetInputRequest->NumRecords = NumEventsRead;
309 return Status;
310 // return STATUS_SUCCESS;
311 }
312 }
313
314
315 /* PUBLIC SERVER APIS *********************************************************/
316
317 CSR_API(SrvReadConsole)
318 {
319 NTSTATUS Status;
320 PCONSOLE_READCONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest;
321 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
322 PVOID HandleEntry;
323 PCONSOLE_INPUT_BUFFER InputBuffer;
324 GET_INPUT_INFO InputInfo;
325
326 DPRINT("SrvReadConsole\n");
327
328 if (!CsrValidateMessageBuffer(ApiMessage,
329 (PVOID*)&ReadConsoleRequest->Buffer,
330 ReadConsoleRequest->BufferSize,
331 sizeof(BYTE)))
332 {
333 return STATUS_INVALID_PARAMETER;
334 }
335
336 if (ReadConsoleRequest->NrCharactersRead > ReadConsoleRequest->NrCharactersToRead)
337 {
338 return STATUS_INVALID_PARAMETER;
339 }
340
341 Status = ConSrvGetInputBufferAndHandleEntry(ProcessData, ReadConsoleRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE);
342 if (!NT_SUCCESS(Status)) return Status;
343
344 // This member is set by the caller (IntReadConsole in kernel32)
345 // ReadConsoleRequest->NrCharactersRead = 0;
346
347 InputInfo.CallingThread = CsrGetClientThread();
348 InputInfo.HandleEntry = HandleEntry;
349 InputInfo.InputBuffer = InputBuffer;
350
351 Status = ReadChars(&InputInfo, ApiMessage, TRUE);
352
353 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
354
355 if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending;
356
357 return Status;
358 }
359
360 CSR_API(SrvGetConsoleInput)
361 {
362 NTSTATUS Status;
363 PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetInputRequest;
364 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
365 PVOID HandleEntry;
366 PCONSOLE_INPUT_BUFFER InputBuffer;
367 GET_INPUT_INFO InputInfo;
368
369 DPRINT("SrvGetConsoleInput\n");
370
371 if (GetInputRequest->Flags & ~(CONSOLE_READ_KEEPEVENT | CONSOLE_READ_CONTINUE))
372 return STATUS_INVALID_PARAMETER;
373
374 /*
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.
379 */
380 if (GetInputRequest->NumRecords <= sizeof(GetInputRequest->RecordStaticBuffer)/sizeof(INPUT_RECORD))
381 {
382 /*
383 * Adjust the internal pointer, because its old value points to
384 * the static buffer in the original ApiMessage structure.
385 */
386 // GetInputRequest->RecordBufPtr = GetInputRequest->RecordStaticBuffer;
387 }
388 else
389 {
390 if (!CsrValidateMessageBuffer(ApiMessage,
391 (PVOID*)&GetInputRequest->RecordBufPtr,
392 GetInputRequest->NumRecords,
393 sizeof(INPUT_RECORD)))
394 {
395 return STATUS_INVALID_PARAMETER;
396 }
397 }
398
399 Status = ConSrvGetInputBufferAndHandleEntry(ProcessData, GetInputRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE);
400 if (!NT_SUCCESS(Status)) return Status;
401
402 InputInfo.CallingThread = CsrGetClientThread();
403 InputInfo.HandleEntry = HandleEntry;
404 InputInfo.InputBuffer = InputBuffer;
405
406 Status = ReadInputBuffer(&InputInfo, ApiMessage, TRUE);
407
408 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
409
410 if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending;
411
412 return Status;
413 }
414
415 NTSTATUS NTAPI
416 ConDrvWriteConsoleInput(IN PCONSOLE Console,
417 IN PCONSOLE_INPUT_BUFFER InputBuffer,
418 IN BOOLEAN Unicode,
419 IN BOOLEAN AppendToEnd,
420 IN PINPUT_RECORD InputRecord,
421 IN ULONG NumEventsToWrite,
422 OUT PULONG NumEventsWritten OPTIONAL);
423 CSR_API(SrvWriteConsoleInput)
424 {
425 NTSTATUS Status;
426 PCONSOLE_WRITEINPUT WriteInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteInputRequest;
427 PCONSOLE_INPUT_BUFFER InputBuffer;
428 ULONG NumEventsWritten;
429
430 PINPUT_RECORD InputRecord;
431
432 DPRINT("SrvWriteConsoleInput\n");
433
434 /*
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.
439 */
440 if (WriteInputRequest->NumRecords <= sizeof(WriteInputRequest->RecordStaticBuffer)/sizeof(INPUT_RECORD))
441 {
442 /*
443 * Adjust the internal pointer, because its old value points to
444 * the static buffer in the original ApiMessage structure.
445 */
446 // WriteInputRequest->RecordBufPtr = WriteInputRequest->RecordStaticBuffer;
447 InputRecord = WriteInputRequest->RecordStaticBuffer;
448 }
449 else
450 {
451 if (!CsrValidateMessageBuffer(ApiMessage,
452 (PVOID*)&WriteInputRequest->RecordBufPtr,
453 WriteInputRequest->NumRecords,
454 sizeof(INPUT_RECORD)))
455 {
456 return STATUS_INVALID_PARAMETER;
457 }
458
459 InputRecord = WriteInputRequest->RecordBufPtr;
460 }
461
462 Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
463 WriteInputRequest->InputHandle,
464 &InputBuffer, GENERIC_WRITE, TRUE);
465 if (!NT_SUCCESS(Status)) return Status;
466
467 NumEventsWritten = 0;
468 Status = ConDrvWriteConsoleInput(InputBuffer->Header.Console,
469 InputBuffer,
470 WriteInputRequest->Unicode,
471 WriteInputRequest->AppendToEnd,
472 InputRecord,
473 WriteInputRequest->NumRecords,
474 &NumEventsWritten);
475 WriteInputRequest->NumRecords = NumEventsWritten;
476
477 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
478 return Status;
479 }
480
481 NTSTATUS NTAPI
482 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
483 IN PCONSOLE_INPUT_BUFFER InputBuffer);
484 CSR_API(SrvFlushConsoleInputBuffer)
485 {
486 NTSTATUS Status;
487 PCONSOLE_FLUSHINPUTBUFFER FlushInputBufferRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.FlushInputBufferRequest;
488 PCONSOLE_INPUT_BUFFER InputBuffer;
489
490 DPRINT("SrvFlushConsoleInputBuffer\n");
491
492 Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
493 FlushInputBufferRequest->InputHandle,
494 &InputBuffer, GENERIC_WRITE, TRUE);
495 if (!NT_SUCCESS(Status)) return Status;
496
497 Status = ConDrvFlushConsoleInputBuffer(InputBuffer->Header.Console,
498 InputBuffer);
499
500 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
501 return Status;
502 }
503
504 NTSTATUS NTAPI
505 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
506 IN PCONSOLE_INPUT_BUFFER InputBuffer,
507 OUT PULONG NumberOfEvents);
508 CSR_API(SrvGetConsoleNumberOfInputEvents)
509 {
510 NTSTATUS Status;
511 PCONSOLE_GETNUMINPUTEVENTS GetNumInputEventsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetNumInputEventsRequest;
512 PCONSOLE_INPUT_BUFFER InputBuffer;
513
514 DPRINT("SrvGetConsoleNumberOfInputEvents\n");
515
516 Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
517 GetNumInputEventsRequest->InputHandle,
518 &InputBuffer, GENERIC_READ, TRUE);
519 if (!NT_SUCCESS(Status)) return Status;
520
521 Status = ConDrvGetConsoleNumberOfInputEvents(InputBuffer->Header.Console,
522 InputBuffer,
523 &GetNumInputEventsRequest->NumberOfEvents);
524
525 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
526 return Status;
527 }
528
529 /* EOF */