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