2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Console Driver DLL
4 * FILE: consrv/condrv/coninput.c
5 * PURPOSE: Console Input functions
6 * PROGRAMMERS: Jeffrey Morlan
7 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10 /* INCLUDES *******************************************************************/
17 /* GLOBALS ********************************************************************/
19 typedef struct ConsoleInput_t
22 INPUT_RECORD InputEvent
;
26 /* PRIVATE FUNCTIONS **********************************************************/
28 // ConDrvAddInputEvents
30 AddInputEvents(PCONSOLE Console
,
31 PINPUT_RECORD InputRecords
, // InputEvent
32 ULONG NumEventsToWrite
,
33 PULONG NumEventsWritten
,
36 NTSTATUS Status
= STATUS_SUCCESS
;
38 BOOLEAN SetWaitEvent
= FALSE
;
40 if (NumEventsWritten
) *NumEventsWritten
= 0;
43 * When adding many single events, in the case of repeated mouse move or
44 * key down events, we try to coalesce them so that we do not saturate
45 * too quickly the input buffer.
47 if (NumEventsToWrite
== 1 && !IsListEmpty(&Console
->InputBuffer
.InputEvents
))
49 PINPUT_RECORD InputRecord
= InputRecords
; // Only one element
50 PINPUT_RECORD LastInputRecord
;
51 ConsoleInput
* ConInRec
; // Input
53 /* Get the "next" event of the input buffer */
56 /* Get the tail element */
57 ConInRec
= CONTAINING_RECORD(Console
->InputBuffer
.InputEvents
.Blink
,
58 ConsoleInput
, ListEntry
);
62 /* Get the head element */
63 ConInRec
= CONTAINING_RECORD(Console
->InputBuffer
.InputEvents
.Flink
,
64 ConsoleInput
, ListEntry
);
66 LastInputRecord
= &ConInRec
->InputEvent
;
68 if (InputRecord
->EventType
== MOUSE_EVENT
&&
69 InputRecord
->Event
.MouseEvent
.dwEventFlags
== MOUSE_MOVED
)
71 if (LastInputRecord
->EventType
== MOUSE_EVENT
&&
72 LastInputRecord
->Event
.MouseEvent
.dwEventFlags
== MOUSE_MOVED
)
74 /* Update the mouse position */
75 LastInputRecord
->Event
.MouseEvent
.dwMousePosition
.X
=
76 InputRecord
->Event
.MouseEvent
.dwMousePosition
.X
;
77 LastInputRecord
->Event
.MouseEvent
.dwMousePosition
.Y
=
78 InputRecord
->Event
.MouseEvent
.dwMousePosition
.Y
;
81 // return STATUS_SUCCESS;
82 Status
= STATUS_SUCCESS
;
85 else if (InputRecord
->EventType
== KEY_EVENT
&&
86 InputRecord
->Event
.KeyEvent
.bKeyDown
)
88 if (LastInputRecord
->EventType
== KEY_EVENT
&&
89 LastInputRecord
->Event
.KeyEvent
.bKeyDown
&&
90 (LastInputRecord
->Event
.KeyEvent
.wVirtualScanCode
== // Same scancode
91 InputRecord
->Event
.KeyEvent
.wVirtualScanCode
) &&
92 (LastInputRecord
->Event
.KeyEvent
.uChar
.UnicodeChar
== // Same character
93 InputRecord
->Event
.KeyEvent
.uChar
.UnicodeChar
) &&
94 (LastInputRecord
->Event
.KeyEvent
.dwControlKeyState
== // Same Ctrl/Alt/Shift state
95 InputRecord
->Event
.KeyEvent
.dwControlKeyState
) )
97 /* Update the repeat count */
98 LastInputRecord
->Event
.KeyEvent
.wRepeatCount
+=
99 InputRecord
->Event
.KeyEvent
.wRepeatCount
;
102 // return STATUS_SUCCESS;
103 Status
= STATUS_SUCCESS
;
108 /* If we coalesced the only one element, we can quit */
109 if (i
== 1 && Status
== STATUS_SUCCESS
/* && NumEventsToWrite == 1 */)
113 * No event coalesced, add them in the usual way.
118 /* Go to the beginning of the list */
119 // InputRecords = InputRecords;
123 /* Go to the end of the list */
124 InputRecords
= &InputRecords
[NumEventsToWrite
- 1];
127 /* Set the event if the list is going to be non-empty */
128 if (IsListEmpty(&Console
->InputBuffer
.InputEvents
))
131 for (i
= 0; i
< NumEventsToWrite
&& NT_SUCCESS(Status
); ++i
)
133 PINPUT_RECORD InputRecord
;
134 ConsoleInput
* ConInRec
;
138 /* Select the event and go to the next one */
139 InputRecord
= InputRecords
++;
143 /* Select the event and go to the previous one */
144 InputRecord
= InputRecords
--;
147 /* Add event to the queue */
148 ConInRec
= ConsoleAllocHeap(0, sizeof(ConsoleInput
));
149 if (ConInRec
== NULL
)
151 // return STATUS_INSUFFICIENT_RESOURCES;
152 Status
= STATUS_INSUFFICIENT_RESOURCES
;
156 ConInRec
->InputEvent
= *InputRecord
;
160 /* Append the event to the end of the queue */
161 InsertTailList(&Console
->InputBuffer
.InputEvents
, &ConInRec
->ListEntry
);
165 /* Append the event to the beginning of the queue */
166 InsertHeadList(&Console
->InputBuffer
.InputEvents
, &ConInRec
->ListEntry
);
169 // return STATUS_SUCCESS;
170 Status
= STATUS_SUCCESS
;
173 if (SetWaitEvent
) NtSetEvent(Console
->InputBuffer
.ActiveEvent
, NULL
);
176 if (NumEventsWritten
) *NumEventsWritten
= i
;
182 PurgeInputBuffer(PCONSOLE Console
)
184 PLIST_ENTRY CurrentEntry
;
187 while (!IsListEmpty(&Console
->InputBuffer
.InputEvents
))
189 CurrentEntry
= RemoveHeadList(&Console
->InputBuffer
.InputEvents
);
190 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
191 ConsoleFreeHeap(Event
);
194 // CloseHandle(Console->InputBuffer.ActiveEvent);
198 ConDrvInitInputBuffer(IN PCONSOLE Console
,
199 IN ULONG InputBufferSize
)
202 OBJECT_ATTRIBUTES ObjectAttributes
;
204 ConSrvInitObject(&Console
->InputBuffer
.Header
, INPUT_BUFFER
, Console
);
206 InitializeObjectAttributes(&ObjectAttributes
,
212 Status
= NtCreateEvent(&Console
->InputBuffer
.ActiveEvent
, EVENT_ALL_ACCESS
,
213 &ObjectAttributes
, NotificationEvent
, FALSE
);
214 if (!NT_SUCCESS(Status
))
216 return STATUS_UNSUCCESSFUL
;
220 Console
->InputBuffer
.InputBufferSize
= InputBufferSize
;
221 InitializeListHead(&Console
->InputBuffer
.InputEvents
);
222 Console
->InputBuffer
.Mode
= ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
|
223 ENABLE_ECHO_INPUT
| ENABLE_MOUSE_INPUT
;
225 return STATUS_SUCCESS
;
229 ConDrvDeinitInputBuffer(IN PCONSOLE Console
)
231 PurgeInputBuffer(Console
);
232 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
236 /* PUBLIC DRIVER APIS *********************************************************/
239 ConDrvReadConsole(IN PCONSOLE Console
,
240 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
243 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl
,
244 IN PVOID Parameter OPTIONAL
,
245 IN ULONG NumCharsToRead
,
246 OUT PULONG NumCharsRead OPTIONAL
)
248 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
249 // NTSTATUS Status; = STATUS_PENDING;
251 if (Console
== NULL
|| InputBuffer
== NULL
|| /* Buffer == NULL || */
252 ReadControl
== NULL
|| ReadControl
->nLength
!= sizeof(CONSOLE_READCONSOLE_CONTROL
))
254 return STATUS_INVALID_PARAMETER
;
257 /* Validity checks */
258 ASSERT(Console
== InputBuffer
->Header
.Console
);
259 ASSERT((Buffer
!= NULL
) || (Buffer
== NULL
&& NumCharsToRead
== 0));
261 /* Call the line-discipline */
262 return TermReadStream(Console
,
272 ConDrvGetConsoleInput(IN PCONSOLE Console
,
273 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
274 IN BOOLEAN KeepEvents
,
275 IN BOOLEAN WaitForMoreEvents
,
276 OUT PINPUT_RECORD InputRecord
,
277 IN ULONG NumEventsToRead
,
278 OUT PULONG NumEventsRead OPTIONAL
)
280 PLIST_ENTRY CurrentInput
;
284 if (Console
== NULL
|| InputBuffer
== NULL
/* || InputRecord == NULL */)
285 return STATUS_INVALID_PARAMETER
;
287 /* Validity checks */
288 ASSERT(Console
== InputBuffer
->Header
.Console
);
289 ASSERT((InputRecord
!= NULL
) || (InputRecord
== NULL
&& NumEventsToRead
== 0));
291 if (NumEventsRead
) *NumEventsRead
= 0;
293 if (IsListEmpty(&InputBuffer
->InputEvents
))
296 * No input is available. Wait for more input if requested,
297 * otherwise, we don't wait, so we return success.
299 return (WaitForMoreEvents
? STATUS_PENDING
: STATUS_SUCCESS
);
302 /* Only get input if there is any */
303 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
305 while ((CurrentInput
!= &InputBuffer
->InputEvents
) && (i
< NumEventsToRead
))
307 Input
= CONTAINING_RECORD(CurrentInput
, ConsoleInput
, ListEntry
);
309 *InputRecord
= Input
->InputEvent
;
313 CurrentInput
= CurrentInput
->Flink
;
315 /* Remove the events from the queue if needed */
318 RemoveEntryList(&Input
->ListEntry
);
319 ConsoleFreeHeap(Input
);
323 if (NumEventsRead
) *NumEventsRead
= i
;
325 if (IsListEmpty(&InputBuffer
->InputEvents
))
327 ResetEvent(InputBuffer
->ActiveEvent
);
330 // FIXME: If we add back UNICODE support, it's here that we need to do the translation.
332 /* We read all the inputs available, we return success */
333 return STATUS_SUCCESS
;
337 ConDrvWriteConsoleInput(IN PCONSOLE Console
,
338 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
339 IN BOOLEAN AppendToEnd
,
340 IN PINPUT_RECORD InputRecord
,
341 IN ULONG NumEventsToWrite
,
342 OUT PULONG NumEventsWritten OPTIONAL
)
344 if (Console
== NULL
|| InputBuffer
== NULL
/* || InputRecord == NULL */)
345 return STATUS_INVALID_PARAMETER
;
347 /* Validity checks */
348 ASSERT(Console
== InputBuffer
->Header
.Console
);
349 ASSERT((InputRecord
!= NULL
) || (InputRecord
== NULL
&& NumEventsToWrite
== 0));
351 /* Now, add the events */
352 if (NumEventsWritten
) *NumEventsWritten
= 0;
354 // FIXME: If we add back UNICODE support, it's here that we need to do the translation.
356 return AddInputEvents(Console
,
364 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console
,
365 IN PCONSOLE_INPUT_BUFFER InputBuffer
)
367 PLIST_ENTRY CurrentEntry
;
370 if (Console
== NULL
|| InputBuffer
== NULL
)
371 return STATUS_INVALID_PARAMETER
;
374 ASSERT(Console
== InputBuffer
->Header
.Console
);
376 /* Discard all entries in the input event queue */
377 while (!IsListEmpty(&InputBuffer
->InputEvents
))
379 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
380 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
381 ConsoleFreeHeap(Event
);
383 ResetEvent(InputBuffer
->ActiveEvent
);
385 return STATUS_SUCCESS
;
389 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console
,
390 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
391 OUT PULONG NumberOfEvents
)
393 PLIST_ENTRY CurrentInput
;
395 if (Console
== NULL
|| InputBuffer
== NULL
|| NumberOfEvents
== NULL
)
396 return STATUS_INVALID_PARAMETER
;
399 ASSERT(Console
== InputBuffer
->Header
.Console
);
403 /* If there are any events ... */
404 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
405 while (CurrentInput
!= &InputBuffer
->InputEvents
)
407 CurrentInput
= CurrentInput
->Flink
;
411 return STATUS_SUCCESS
;