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