ab43adcd4701827d5c169938487c59a75d7745ee
[reactos.git] / win32ss / user / winsrv / consrv / condrv / coninput.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Driver DLL
4 * FILE: win32ss/user/winsrv/consrv/condrv/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 /*
20 * From MSDN:
21 * "The lpMultiByteStr and lpWideCharStr pointers must not be the same.
22 * If they are the same, the function fails, and GetLastError returns
23 * ERROR_INVALID_PARAMETER."
24 */
25 #define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
26 ASSERT((ULONG_PTR)dChar != (ULONG_PTR)sWChar); \
27 WideCharToMultiByte((Console)->InputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
28
29 #define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
30 ASSERT((ULONG_PTR)dWChar != (ULONG_PTR)sChar); \
31 MultiByteToWideChar((Console)->InputCodePage, 0, (sChar), 1, (dWChar), 1)
32
33 typedef struct ConsoleInput_t
34 {
35 LIST_ENTRY ListEntry;
36 INPUT_RECORD InputEvent;
37 } ConsoleInput;
38
39
40 /* PRIVATE FUNCTIONS **********************************************************/
41
42 static VOID
43 ConioInputEventToAnsi(PCONSOLE Console, PINPUT_RECORD InputEvent)
44 {
45 if (InputEvent->EventType == KEY_EVENT)
46 {
47 WCHAR UnicodeChar = InputEvent->Event.KeyEvent.uChar.UnicodeChar;
48 InputEvent->Event.KeyEvent.uChar.UnicodeChar = 0;
49 ConsoleInputUnicodeCharToAnsiChar(Console,
50 &InputEvent->Event.KeyEvent.uChar.AsciiChar,
51 &UnicodeChar);
52 }
53 }
54
55 static VOID
56 ConioInputEventToUnicode(PCONSOLE Console, PINPUT_RECORD InputEvent)
57 {
58 if (InputEvent->EventType == KEY_EVENT)
59 {
60 CHAR AsciiChar = InputEvent->Event.KeyEvent.uChar.AsciiChar;
61 InputEvent->Event.KeyEvent.uChar.AsciiChar = 0;
62 ConsoleInputAnsiCharToUnicodeChar(Console,
63 &InputEvent->Event.KeyEvent.uChar.UnicodeChar,
64 &AsciiChar);
65 }
66 }
67
68
69 NTSTATUS
70 ConDrvAddInputEvents(PCONSOLE Console,
71 PINPUT_RECORD InputRecords, // InputEvent
72 ULONG NumEventsToWrite,
73 PULONG NumEventsWritten,
74 BOOLEAN AppendToEnd)
75 {
76 NTSTATUS Status = STATUS_SUCCESS;
77 ULONG i = 0;
78 BOOLEAN SetWaitEvent = FALSE;
79
80 if (NumEventsWritten) *NumEventsWritten = 0;
81
82 /*
83 * When adding many single events, in the case of repeated mouse move or
84 * key down events, we try to coalesce them so that we do not saturate
85 * too quickly the input buffer.
86 */
87 if (NumEventsToWrite == 1 && !IsListEmpty(&Console->InputBuffer.InputEvents))
88 {
89 PINPUT_RECORD InputRecord = InputRecords; // Only one element
90 PINPUT_RECORD LastInputRecord;
91 ConsoleInput* ConInRec; // Input
92
93 /* Get the "next" event of the input buffer */
94 if (AppendToEnd)
95 {
96 /* Get the tail element */
97 ConInRec = CONTAINING_RECORD(Console->InputBuffer.InputEvents.Blink,
98 ConsoleInput, ListEntry);
99 }
100 else
101 {
102 /* Get the head element */
103 ConInRec = CONTAINING_RECORD(Console->InputBuffer.InputEvents.Flink,
104 ConsoleInput, ListEntry);
105 }
106 LastInputRecord = &ConInRec->InputEvent;
107
108 if (InputRecord->EventType == MOUSE_EVENT &&
109 InputRecord->Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
110 {
111 if (LastInputRecord->EventType == MOUSE_EVENT &&
112 LastInputRecord->Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
113 {
114 /* Update the mouse position */
115 LastInputRecord->Event.MouseEvent.dwMousePosition.X =
116 InputRecord->Event.MouseEvent.dwMousePosition.X;
117 LastInputRecord->Event.MouseEvent.dwMousePosition.Y =
118 InputRecord->Event.MouseEvent.dwMousePosition.Y;
119
120 i = 1;
121 // return STATUS_SUCCESS;
122 Status = STATUS_SUCCESS;
123 }
124 }
125 else if (InputRecord->EventType == KEY_EVENT &&
126 InputRecord->Event.KeyEvent.bKeyDown)
127 {
128 if (LastInputRecord->EventType == KEY_EVENT &&
129 LastInputRecord->Event.KeyEvent.bKeyDown &&
130 (LastInputRecord->Event.KeyEvent.wVirtualScanCode == // Same scancode
131 InputRecord->Event.KeyEvent.wVirtualScanCode) &&
132 (LastInputRecord->Event.KeyEvent.uChar.UnicodeChar == // Same character
133 InputRecord->Event.KeyEvent.uChar.UnicodeChar) &&
134 (LastInputRecord->Event.KeyEvent.dwControlKeyState == // Same Ctrl/Alt/Shift state
135 InputRecord->Event.KeyEvent.dwControlKeyState) )
136 {
137 /* Update the repeat count */
138 LastInputRecord->Event.KeyEvent.wRepeatCount +=
139 InputRecord->Event.KeyEvent.wRepeatCount;
140
141 i = 1;
142 // return STATUS_SUCCESS;
143 Status = STATUS_SUCCESS;
144 }
145 }
146 }
147
148 /* If we coalesced the only one element, we can quit */
149 if (i == 1 && Status == STATUS_SUCCESS /* && NumEventsToWrite == 1 */)
150 goto Done;
151
152 /*
153 * No event coalesced, add them in the usual way.
154 */
155
156 if (AppendToEnd)
157 {
158 /* Go to the beginning of the list */
159 // InputRecords = InputRecords;
160 }
161 else
162 {
163 /* Go to the end of the list */
164 InputRecords = &InputRecords[NumEventsToWrite - 1];
165 }
166
167 /* Set the event if the list is going to be non-empty */
168 if (IsListEmpty(&Console->InputBuffer.InputEvents))
169 SetWaitEvent = TRUE;
170
171 for (i = 0; i < NumEventsToWrite && NT_SUCCESS(Status); ++i)
172 {
173 PINPUT_RECORD InputRecord;
174 ConsoleInput* ConInRec;
175
176 if (AppendToEnd)
177 {
178 /* Select the event and go to the next one */
179 InputRecord = InputRecords++;
180 }
181 else
182 {
183 /* Select the event and go to the previous one */
184 InputRecord = InputRecords--;
185 }
186
187 /* Add event to the queue */
188 ConInRec = ConsoleAllocHeap(0, sizeof(ConsoleInput));
189 if (ConInRec == NULL)
190 {
191 // return STATUS_INSUFFICIENT_RESOURCES;
192 Status = STATUS_INSUFFICIENT_RESOURCES;
193 continue;
194 }
195
196 ConInRec->InputEvent = *InputRecord;
197
198 if (AppendToEnd)
199 {
200 /* Append the event to the end of the queue */
201 InsertTailList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry);
202 }
203 else
204 {
205 /* Append the event to the beginning of the queue */
206 InsertHeadList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry);
207 }
208
209 // return STATUS_SUCCESS;
210 Status = STATUS_SUCCESS;
211 }
212
213 if (SetWaitEvent) SetEvent(Console->InputBuffer.ActiveEvent);
214
215 Done:
216 if (NumEventsWritten) *NumEventsWritten = i;
217
218 return Status;
219 }
220
221
222 ULONG
223 PreprocessInput(PCONSOLE Console,
224 PINPUT_RECORD InputEvent,
225 ULONG NumEventsToWrite);
226 VOID
227 PostprocessInput(PCONSOLE Console);
228
229 NTSTATUS
230 ConioAddInputEvents(PCONSOLE Console,
231 PINPUT_RECORD InputRecords, // InputEvent
232 ULONG NumEventsToWrite,
233 PULONG NumEventsWritten,
234 BOOLEAN AppendToEnd)
235 {
236 NTSTATUS Status = STATUS_SUCCESS;
237
238 if (NumEventsWritten) *NumEventsWritten = 0;
239
240 /*
241 * This pre-processing code MUST be IN consrv ONLY!!
242 */
243 NumEventsToWrite = PreprocessInput(Console, InputRecords, NumEventsToWrite);
244 if (NumEventsToWrite == 0) return STATUS_SUCCESS;
245
246 Status = ConDrvAddInputEvents(Console,
247 InputRecords,
248 NumEventsToWrite,
249 NumEventsWritten,
250 AppendToEnd);
251
252 /*
253 * This post-processing code MUST be IN consrv ONLY!!
254 */
255 // if (NT_SUCCESS(Status))
256 if (Status == STATUS_SUCCESS) PostprocessInput(Console);
257
258 return Status;
259 }
260
261 /* Move elsewhere...*/
262 NTSTATUS
263 ConioProcessInputEvent(PCONSOLE Console,
264 PINPUT_RECORD InputEvent)
265 {
266 ULONG NumEventsWritten;
267 return ConioAddInputEvents(Console,
268 InputEvent,
269 1,
270 &NumEventsWritten,
271 TRUE);
272 }
273
274
275 VOID
276 PurgeInputBuffer(PCONSOLE Console)
277 {
278 PLIST_ENTRY CurrentEntry;
279 ConsoleInput* Event;
280
281 while (!IsListEmpty(&Console->InputBuffer.InputEvents))
282 {
283 CurrentEntry = RemoveHeadList(&Console->InputBuffer.InputEvents);
284 Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
285 ConsoleFreeHeap(Event);
286 }
287
288 CloseHandle(Console->InputBuffer.ActiveEvent);
289 }
290
291
292 /* PUBLIC DRIVER APIS *********************************************************/
293
294 NTSTATUS NTAPI
295 ConDrvReadConsole(IN PCONSOLE Console,
296 IN PCONSOLE_INPUT_BUFFER InputBuffer,
297 /**/IN PUNICODE_STRING ExeName /**/OPTIONAL/**/,/**/
298 IN BOOLEAN Unicode,
299 OUT PVOID Buffer,
300 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
301 IN ULONG NumCharsToRead,
302 OUT PULONG NumCharsRead OPTIONAL)
303 {
304 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
305 // NTSTATUS Status; = STATUS_PENDING;
306
307 if (Console == NULL || InputBuffer == NULL || /* Buffer == NULL || */
308 ReadControl == NULL || ReadControl->nLength != sizeof(CONSOLE_READCONSOLE_CONTROL))
309 {
310 return STATUS_INVALID_PARAMETER;
311 }
312
313 /* Validity checks */
314 ASSERT(Console == InputBuffer->Header.Console);
315 ASSERT((Buffer != NULL) || (Buffer == NULL && NumCharsToRead == 0));
316
317 /* Call the line-discipline */
318 return TermReadStream(Console,
319 ExeName,
320 Unicode,
321 Buffer,
322 ReadControl,
323 NumCharsToRead,
324 NumCharsRead);
325 }
326
327 NTSTATUS NTAPI
328 ConDrvGetConsoleInput(IN PCONSOLE Console,
329 IN PCONSOLE_INPUT_BUFFER InputBuffer,
330 IN BOOLEAN KeepEvents,
331 IN BOOLEAN WaitForMoreEvents,
332 IN BOOLEAN Unicode,
333 OUT PINPUT_RECORD InputRecord,
334 IN ULONG NumEventsToRead,
335 OUT PULONG NumEventsRead OPTIONAL)
336 {
337 PLIST_ENTRY CurrentInput;
338 ConsoleInput* Input;
339 ULONG i = 0;
340
341 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */)
342 return STATUS_INVALID_PARAMETER;
343
344 /* Validity checks */
345 ASSERT(Console == InputBuffer->Header.Console);
346 ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToRead == 0));
347
348 if (NumEventsRead) *NumEventsRead = 0;
349
350 if (IsListEmpty(&InputBuffer->InputEvents))
351 {
352 /*
353 * No input is available. Wait for more input if requested,
354 * otherwise, we don't wait, so we return success.
355 */
356 return (WaitForMoreEvents ? STATUS_PENDING : STATUS_SUCCESS);
357 }
358
359 /* Only get input if there is any */
360 CurrentInput = InputBuffer->InputEvents.Flink;
361 i = 0;
362 while ((CurrentInput != &InputBuffer->InputEvents) && (i < NumEventsToRead))
363 {
364 Input = CONTAINING_RECORD(CurrentInput, ConsoleInput, ListEntry);
365
366 *InputRecord = Input->InputEvent;
367
368 ++InputRecord;
369 ++i;
370 CurrentInput = CurrentInput->Flink;
371
372 /* Remove the events from the queue if needed */
373 if (!KeepEvents)
374 {
375 RemoveEntryList(&Input->ListEntry);
376 ConsoleFreeHeap(Input);
377 }
378 }
379
380 if (NumEventsRead) *NumEventsRead = i;
381
382 /* Now translate everything to ANSI */
383 if (!Unicode)
384 {
385 for (; i > 0; --i)
386 {
387 ConioInputEventToAnsi(InputBuffer->Header.Console, --InputRecord);
388 }
389 }
390
391 if (IsListEmpty(&InputBuffer->InputEvents))
392 {
393 ResetEvent(InputBuffer->ActiveEvent);
394 }
395
396 /* We read all the inputs available, we return success */
397 return STATUS_SUCCESS;
398 }
399
400 NTSTATUS NTAPI
401 ConDrvWriteConsoleInput(IN PCONSOLE Console,
402 IN PCONSOLE_INPUT_BUFFER InputBuffer,
403 IN BOOLEAN Unicode,
404 IN BOOLEAN AppendToEnd,
405 IN PINPUT_RECORD InputRecord,
406 IN ULONG NumEventsToWrite,
407 OUT PULONG NumEventsWritten OPTIONAL)
408 {
409 NTSTATUS Status = STATUS_SUCCESS;
410 ULONG i;
411
412 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */)
413 return STATUS_INVALID_PARAMETER;
414
415 /* Validity checks */
416 ASSERT(Console == InputBuffer->Header.Console);
417 ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToWrite == 0));
418
419 /* First translate everything to UNICODE */
420 if (!Unicode)
421 {
422 for (i = 0; i < NumEventsToWrite; ++i)
423 {
424 ConioInputEventToUnicode(Console, &InputRecord[i]);
425 }
426 }
427
428 /* Now, add the events */
429 // if (NumEventsWritten) *NumEventsWritten = 0;
430 // ConDrvAddInputEvents
431 Status = ConioAddInputEvents(Console,
432 InputRecord,
433 NumEventsToWrite,
434 NumEventsWritten,
435 AppendToEnd);
436 // if (NumEventsWritten) *NumEventsWritten = i;
437
438 return Status;
439 }
440
441 NTSTATUS NTAPI
442 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
443 IN PCONSOLE_INPUT_BUFFER InputBuffer)
444 {
445 PLIST_ENTRY CurrentEntry;
446 ConsoleInput* Event;
447
448 if (Console == NULL || InputBuffer == NULL)
449 return STATUS_INVALID_PARAMETER;
450
451 /* Validity check */
452 ASSERT(Console == InputBuffer->Header.Console);
453
454 /* Discard all entries in the input event queue */
455 while (!IsListEmpty(&InputBuffer->InputEvents))
456 {
457 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
458 Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
459 ConsoleFreeHeap(Event);
460 }
461 ResetEvent(InputBuffer->ActiveEvent);
462
463 return STATUS_SUCCESS;
464 }
465
466 NTSTATUS NTAPI
467 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
468 IN PCONSOLE_INPUT_BUFFER InputBuffer,
469 OUT PULONG NumberOfEvents)
470 {
471 PLIST_ENTRY CurrentInput;
472
473 if (Console == NULL || InputBuffer == NULL || NumberOfEvents == NULL)
474 return STATUS_INVALID_PARAMETER;
475
476 /* Validity check */
477 ASSERT(Console == InputBuffer->Header.Console);
478
479 *NumberOfEvents = 0;
480
481 /* If there are any events ... */
482 CurrentInput = InputBuffer->InputEvents.Flink;
483 while (CurrentInput != &InputBuffer->InputEvents)
484 {
485 CurrentInput = CurrentInput->Flink;
486 (*NumberOfEvents)++;
487 }
488
489 return STATUS_SUCCESS;
490 }
491
492 /* EOF */