[CONSRV-WINSRV]
[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/conio2.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 NULL))
65 {
66 ConsoleFreeHeap(CapturedInputInfo);
67 return STATUS_NO_MEMORY;
68 }
69 }
70
71 /* Wait for input */
72 return STATUS_PENDING;
73 }
74
75 static NTSTATUS
76 ReadChars(IN PGET_INPUT_INFO InputInfo,
77 IN PCSR_API_MESSAGE ApiMessage,
78 IN BOOL CreateWaitBlock OPTIONAL);
79
80 // Wait function CSR_WAIT_FUNCTION
81 static BOOLEAN
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 ReadInputBufferThread(IN PLIST_ENTRY WaitList,
198 IN PCSR_THREAD WaitThread,
199 IN PCSR_API_MESSAGE WaitApiMessage,
200 IN PVOID WaitContext,
201 IN PVOID WaitArgument1,
202 IN PVOID WaitArgument2,
203 IN ULONG WaitFlags)
204 {
205 NTSTATUS Status;
206 PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)WaitApiMessage)->Data.GetInputRequest;
207 PGET_INPUT_INFO InputInfo = (PGET_INPUT_INFO)WaitContext;
208
209 PVOID InputHandle = WaitArgument2;
210
211 DPRINT("ReadInputBufferThread - WaitContext = 0x%p, WaitArgument1 = 0x%p, WaitArgument2 = 0x%p, WaitFlags = %lu\n", WaitContext, WaitArgument1, WaitArgument2, WaitFlags);
212
213 /*
214 * If we are notified of the process termination via a call
215 * to CsrNotifyWaitBlock triggered by CsrDestroyProcess or
216 * CsrDestroyThread, just return.
217 */
218 if (WaitFlags & CsrProcessTerminating)
219 {
220 Status = STATUS_THREAD_IS_TERMINATING;
221 goto Quit;
222 }
223
224 /*
225 * Somebody is closing a handle to this input buffer,
226 * by calling ConSrvCloseHandleEntry.
227 * See whether we are linked to that handle (ie. we
228 * are a waiter for this handle), and if so, return.
229 * Otherwise, ignore the call and continue waiting.
230 */
231 if (InputHandle != NULL)
232 {
233 Status = (InputHandle == InputInfo->HandleEntry ? STATUS_ALERTED
234 : STATUS_PENDING);
235 goto Quit;
236 }
237
238 /*
239 * If we go there, that means we are notified for some new input.
240 * The console is therefore already locked.
241 */
242 Status = ReadInputBuffer(InputInfo,
243 GetInputRequest->bRead,
244 WaitApiMessage,
245 FALSE);
246
247 Quit:
248 if (Status != STATUS_PENDING)
249 {
250 WaitApiMessage->Status = Status;
251 ConsoleFreeHeap(InputInfo);
252 }
253
254 return (Status == STATUS_PENDING ? FALSE : TRUE);
255 }
256
257 NTSTATUS NTAPI
258 ConDrvGetConsoleInput(IN PCONSOLE Console,
259 IN PCONSOLE_INPUT_BUFFER InputBuffer,
260 IN BOOLEAN WaitForMoreEvents,
261 IN BOOLEAN Unicode,
262 OUT PINPUT_RECORD InputRecord,
263 IN ULONG NumEventsToRead,
264 OUT PULONG NumEventsRead);
265 static NTSTATUS
266 ReadInputBuffer(IN PGET_INPUT_INFO InputInfo,
267 IN BOOL Wait, // TRUE --> Read ; FALSE --> Peek
268 IN PCSR_API_MESSAGE ApiMessage,
269 IN BOOL CreateWaitBlock OPTIONAL)
270 {
271 NTSTATUS Status;
272 PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetInputRequest;
273 PCONSOLE_INPUT_BUFFER InputBuffer = InputInfo->InputBuffer;
274
275 // GetInputRequest->InputsRead = 0;
276
277 Status = ConDrvGetConsoleInput(InputBuffer->Header.Console,
278 InputBuffer,
279 Wait,
280 GetInputRequest->Unicode,
281 GetInputRequest->InputRecord,
282 GetInputRequest->Length,
283 &GetInputRequest->InputsRead);
284
285 if (Status == STATUS_PENDING)
286 {
287 /* We haven't completed a read, so start a wait */
288 return WaitBeforeReading(InputInfo,
289 ApiMessage,
290 ReadInputBufferThread,
291 CreateWaitBlock);
292 }
293 else
294 {
295 /* We read all what we wanted, we return the error code we were given */
296 return Status;
297 // return STATUS_SUCCESS;
298 }
299 }
300
301
302 /* PUBLIC SERVER APIS *********************************************************/
303
304 CSR_API(SrvReadConsole)
305 {
306 NTSTATUS Status;
307 PCONSOLE_READCONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest;
308 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
309 PVOID HandleEntry;
310 PCONSOLE_INPUT_BUFFER InputBuffer;
311 GET_INPUT_INFO InputInfo;
312
313 DPRINT("SrvReadConsole\n");
314
315 if (!CsrValidateMessageBuffer(ApiMessage,
316 (PVOID*)&ReadConsoleRequest->Buffer,
317 ReadConsoleRequest->BufferSize,
318 sizeof(BYTE)))
319 {
320 return STATUS_INVALID_PARAMETER;
321 }
322
323 if (ReadConsoleRequest->NrCharactersRead > ReadConsoleRequest->NrCharactersToRead)
324 {
325 return STATUS_INVALID_PARAMETER;
326 }
327
328 Status = ConSrvGetInputBufferAndHandleEntry(ProcessData, ReadConsoleRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE);
329 if (!NT_SUCCESS(Status)) return Status;
330
331 // This member is set by the caller (IntReadConsole in kernel32)
332 // ReadConsoleRequest->NrCharactersRead = 0;
333
334 InputInfo.CallingThread = CsrGetClientThread();
335 InputInfo.HandleEntry = HandleEntry;
336 InputInfo.InputBuffer = InputBuffer;
337
338 Status = ReadChars(&InputInfo,
339 ApiMessage,
340 TRUE);
341
342 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
343
344 if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending;
345
346 return Status;
347 }
348
349 CSR_API(SrvGetConsoleInput)
350 {
351 NTSTATUS Status;
352 PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetInputRequest;
353 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
354 PVOID HandleEntry;
355 PCONSOLE_INPUT_BUFFER InputBuffer;
356 GET_INPUT_INFO InputInfo;
357
358 DPRINT("SrvGetConsoleInput\n");
359
360 if (!CsrValidateMessageBuffer(ApiMessage,
361 (PVOID*)&GetInputRequest->InputRecord,
362 GetInputRequest->Length,
363 sizeof(INPUT_RECORD)))
364 {
365 return STATUS_INVALID_PARAMETER;
366 }
367
368 Status = ConSrvGetInputBufferAndHandleEntry(ProcessData, GetInputRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE);
369 if (!NT_SUCCESS(Status)) return Status;
370
371 GetInputRequest->InputsRead = 0;
372
373 InputInfo.CallingThread = CsrGetClientThread();
374 InputInfo.HandleEntry = HandleEntry;
375 InputInfo.InputBuffer = InputBuffer;
376
377 Status = ReadInputBuffer(&InputInfo,
378 GetInputRequest->bRead,
379 ApiMessage,
380 TRUE);
381
382 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
383
384 if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending;
385
386 return Status;
387 }
388
389 NTSTATUS NTAPI
390 ConDrvWriteConsoleInput(IN PCONSOLE Console,
391 IN PCONSOLE_INPUT_BUFFER InputBuffer,
392 IN BOOLEAN Unicode,
393 IN PINPUT_RECORD InputRecord,
394 IN ULONG NumEventsToWrite,
395 OUT PULONG NumEventsWritten OPTIONAL);
396 CSR_API(SrvWriteConsoleInput)
397 {
398 NTSTATUS Status;
399 PCONSOLE_WRITEINPUT WriteInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteInputRequest;
400 PCONSOLE_INPUT_BUFFER InputBuffer;
401 ULONG NumEventsWritten;
402
403 DPRINT("SrvWriteConsoleInput\n");
404
405 if (!CsrValidateMessageBuffer(ApiMessage,
406 (PVOID*)&WriteInputRequest->InputRecord,
407 WriteInputRequest->Length,
408 sizeof(INPUT_RECORD)))
409 {
410 return STATUS_INVALID_PARAMETER;
411 }
412
413 Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
414 WriteInputRequest->InputHandle,
415 &InputBuffer, GENERIC_WRITE, TRUE);
416 if (!NT_SUCCESS(Status)) return Status;
417
418 NumEventsWritten = 0;
419 Status = ConDrvWriteConsoleInput(InputBuffer->Header.Console,
420 InputBuffer,
421 WriteInputRequest->Unicode,
422 WriteInputRequest->InputRecord,
423 WriteInputRequest->Length,
424 &NumEventsWritten);
425 WriteInputRequest->Length = NumEventsWritten;
426
427 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
428 return Status;
429 }
430
431 NTSTATUS NTAPI
432 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
433 IN PCONSOLE_INPUT_BUFFER InputBuffer);
434 CSR_API(SrvFlushConsoleInputBuffer)
435 {
436 NTSTATUS Status;
437 PCONSOLE_FLUSHINPUTBUFFER FlushInputBufferRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.FlushInputBufferRequest;
438 PCONSOLE_INPUT_BUFFER InputBuffer;
439
440 DPRINT("SrvFlushConsoleInputBuffer\n");
441
442 Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
443 FlushInputBufferRequest->InputHandle,
444 &InputBuffer, GENERIC_WRITE, TRUE);
445 if (!NT_SUCCESS(Status)) return Status;
446
447 Status = ConDrvFlushConsoleInputBuffer(InputBuffer->Header.Console,
448 InputBuffer);
449
450 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
451 return Status;
452 }
453
454 NTSTATUS NTAPI
455 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
456 IN PCONSOLE_INPUT_BUFFER InputBuffer,
457 OUT PULONG NumEvents);
458 CSR_API(SrvGetConsoleNumberOfInputEvents)
459 {
460 NTSTATUS Status;
461 PCONSOLE_GETNUMINPUTEVENTS GetNumInputEventsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetNumInputEventsRequest;
462 PCONSOLE_INPUT_BUFFER InputBuffer;
463
464 DPRINT("SrvGetConsoleNumberOfInputEvents\n");
465
466 Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
467 GetNumInputEventsRequest->InputHandle,
468 &InputBuffer, GENERIC_READ, TRUE);
469 if (!NT_SUCCESS(Status)) return Status;
470
471 Status = ConDrvGetConsoleNumberOfInputEvents(InputBuffer->Header.Console,
472 InputBuffer,
473 &GetNumInputEventsRequest->NumInputEvents);
474
475 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
476 return Status;
477 }
478
479 /* EOF */