* Sync up to trunk HEAD (r62285). Branch guys deserve the significant speedups too ;)
[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->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
264 // GetInputRequest->InputsRead = 0;
265
266 Status = ConDrvGetConsoleInput(InputBuffer->Header.Console,
267 InputBuffer,
268 (GetInputRequest->wFlags & CONSOLE_READ_KEEPEVENT) != 0,
269 (GetInputRequest->wFlags & CONSOLE_READ_CONTINUE ) == 0,
270 GetInputRequest->Unicode,
271 GetInputRequest->InputRecord,
272 GetInputRequest->Length,
273 &GetInputRequest->InputsRead);
274
275 if (Status == STATUS_PENDING)
276 {
277 /* We haven't completed a read, so start a wait */
278 return WaitBeforeReading(InputInfo,
279 ApiMessage,
280 ReadInputBufferThread,
281 CreateWaitBlock);
282 }
283 else
284 {
285 /* We read all what we wanted, we return the error code we were given */
286 return Status;
287 // return STATUS_SUCCESS;
288 }
289 }
290
291
292 /* PUBLIC SERVER APIS *********************************************************/
293
294 CSR_API(SrvReadConsole)
295 {
296 NTSTATUS Status;
297 PCONSOLE_READCONSOLE ReadConsoleRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ReadConsoleRequest;
298 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
299 PVOID HandleEntry;
300 PCONSOLE_INPUT_BUFFER InputBuffer;
301 GET_INPUT_INFO InputInfo;
302
303 DPRINT("SrvReadConsole\n");
304
305 if (!CsrValidateMessageBuffer(ApiMessage,
306 (PVOID*)&ReadConsoleRequest->Buffer,
307 ReadConsoleRequest->BufferSize,
308 sizeof(BYTE)))
309 {
310 return STATUS_INVALID_PARAMETER;
311 }
312
313 if (ReadConsoleRequest->NrCharactersRead > ReadConsoleRequest->NrCharactersToRead)
314 {
315 return STATUS_INVALID_PARAMETER;
316 }
317
318 Status = ConSrvGetInputBufferAndHandleEntry(ProcessData, ReadConsoleRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE);
319 if (!NT_SUCCESS(Status)) return Status;
320
321 // This member is set by the caller (IntReadConsole in kernel32)
322 // ReadConsoleRequest->NrCharactersRead = 0;
323
324 InputInfo.CallingThread = CsrGetClientThread();
325 InputInfo.HandleEntry = HandleEntry;
326 InputInfo.InputBuffer = InputBuffer;
327
328 Status = ReadChars(&InputInfo, ApiMessage, TRUE);
329
330 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
331
332 if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending;
333
334 return Status;
335 }
336
337 CSR_API(SrvGetConsoleInput)
338 {
339 NTSTATUS Status;
340 PCONSOLE_GETINPUT GetInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetInputRequest;
341 PCONSOLE_PROCESS_DATA ProcessData = ConsoleGetPerProcessData(CsrGetClientThread()->Process);
342 PVOID HandleEntry;
343 PCONSOLE_INPUT_BUFFER InputBuffer;
344 GET_INPUT_INFO InputInfo;
345
346 DPRINT("SrvGetConsoleInput\n");
347
348 if (GetInputRequest->wFlags & ~(CONSOLE_READ_KEEPEVENT | CONSOLE_READ_CONTINUE))
349 return STATUS_INVALID_PARAMETER;
350
351 if (!CsrValidateMessageBuffer(ApiMessage,
352 (PVOID*)&GetInputRequest->InputRecord,
353 GetInputRequest->Length,
354 sizeof(INPUT_RECORD)))
355 {
356 return STATUS_INVALID_PARAMETER;
357 }
358
359 Status = ConSrvGetInputBufferAndHandleEntry(ProcessData, GetInputRequest->InputHandle, &InputBuffer, &HandleEntry, GENERIC_READ, TRUE);
360 if (!NT_SUCCESS(Status)) return Status;
361
362 GetInputRequest->InputsRead = 0;
363
364 InputInfo.CallingThread = CsrGetClientThread();
365 InputInfo.HandleEntry = HandleEntry;
366 InputInfo.InputBuffer = InputBuffer;
367
368 Status = ReadInputBuffer(&InputInfo, ApiMessage, TRUE);
369
370 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
371
372 if (Status == STATUS_PENDING) *ReplyCode = CsrReplyPending;
373
374 return Status;
375 }
376
377 NTSTATUS NTAPI
378 ConDrvWriteConsoleInput(IN PCONSOLE Console,
379 IN PCONSOLE_INPUT_BUFFER InputBuffer,
380 IN BOOLEAN Unicode,
381 IN BOOLEAN AppendToEnd,
382 IN PINPUT_RECORD InputRecord,
383 IN ULONG NumEventsToWrite,
384 OUT PULONG NumEventsWritten OPTIONAL);
385 CSR_API(SrvWriteConsoleInput)
386 {
387 NTSTATUS Status;
388 PCONSOLE_WRITEINPUT WriteInputRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.WriteInputRequest;
389 PCONSOLE_INPUT_BUFFER InputBuffer;
390 ULONG NumEventsWritten;
391
392 DPRINT("SrvWriteConsoleInput\n");
393
394 if (!CsrValidateMessageBuffer(ApiMessage,
395 (PVOID*)&WriteInputRequest->InputRecord,
396 WriteInputRequest->Length,
397 sizeof(INPUT_RECORD)))
398 {
399 return STATUS_INVALID_PARAMETER;
400 }
401
402 Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
403 WriteInputRequest->InputHandle,
404 &InputBuffer, GENERIC_WRITE, TRUE);
405 if (!NT_SUCCESS(Status)) return Status;
406
407 NumEventsWritten = 0;
408 Status = ConDrvWriteConsoleInput(InputBuffer->Header.Console,
409 InputBuffer,
410 WriteInputRequest->Unicode,
411 WriteInputRequest->AppendToEnd,
412 WriteInputRequest->InputRecord,
413 WriteInputRequest->Length,
414 &NumEventsWritten);
415 WriteInputRequest->Length = NumEventsWritten;
416
417 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
418 return Status;
419 }
420
421 NTSTATUS NTAPI
422 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
423 IN PCONSOLE_INPUT_BUFFER InputBuffer);
424 CSR_API(SrvFlushConsoleInputBuffer)
425 {
426 NTSTATUS Status;
427 PCONSOLE_FLUSHINPUTBUFFER FlushInputBufferRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.FlushInputBufferRequest;
428 PCONSOLE_INPUT_BUFFER InputBuffer;
429
430 DPRINT("SrvFlushConsoleInputBuffer\n");
431
432 Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
433 FlushInputBufferRequest->InputHandle,
434 &InputBuffer, GENERIC_WRITE, TRUE);
435 if (!NT_SUCCESS(Status)) return Status;
436
437 Status = ConDrvFlushConsoleInputBuffer(InputBuffer->Header.Console,
438 InputBuffer);
439
440 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
441 return Status;
442 }
443
444 NTSTATUS NTAPI
445 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
446 IN PCONSOLE_INPUT_BUFFER InputBuffer,
447 OUT PULONG NumEvents);
448 CSR_API(SrvGetConsoleNumberOfInputEvents)
449 {
450 NTSTATUS Status;
451 PCONSOLE_GETNUMINPUTEVENTS GetNumInputEventsRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetNumInputEventsRequest;
452 PCONSOLE_INPUT_BUFFER InputBuffer;
453
454 DPRINT("SrvGetConsoleNumberOfInputEvents\n");
455
456 Status = ConSrvGetInputBuffer(ConsoleGetPerProcessData(CsrGetClientThread()->Process),
457 GetNumInputEventsRequest->InputHandle,
458 &InputBuffer, GENERIC_READ, TRUE);
459 if (!NT_SUCCESS(Status)) return Status;
460
461 Status = ConDrvGetConsoleNumberOfInputEvents(InputBuffer->Header.Console,
462 InputBuffer,
463 &GetNumInputEventsRequest->NumInputEvents);
464
465 ConSrvReleaseInputBuffer(InputBuffer, TRUE);
466 return Status;
467 }
468
469 /* EOF */