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