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)
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
))
217 Console
->InputBuffer
.InputBufferSize
= InputBufferSize
;
218 InitializeListHead(&Console
->InputBuffer
.InputEvents
);
219 Console
->InputBuffer
.Mode
= ENABLE_PROCESSED_INPUT
| ENABLE_LINE_INPUT
|
220 ENABLE_ECHO_INPUT
| ENABLE_MOUSE_INPUT
;
222 return STATUS_SUCCESS
;
226 ConDrvDeinitInputBuffer(IN PCONSOLE Console
)
228 PurgeInputBuffer(Console
);
229 CloseHandle(Console
->InputBuffer
.ActiveEvent
);
233 /* PUBLIC DRIVER APIS *********************************************************/
236 ConDrvReadConsole(IN PCONSOLE Console
,
237 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
240 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl
,
241 IN PVOID Parameter OPTIONAL
,
242 IN ULONG NumCharsToRead
,
243 OUT PULONG NumCharsRead OPTIONAL
)
245 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
246 // NTSTATUS Status; = STATUS_PENDING;
248 if (Console
== NULL
|| InputBuffer
== NULL
|| /* Buffer == NULL || */
249 ReadControl
== NULL
|| ReadControl
->nLength
!= sizeof(CONSOLE_READCONSOLE_CONTROL
))
251 return STATUS_INVALID_PARAMETER
;
254 /* Validity checks */
255 ASSERT(Console
== InputBuffer
->Header
.Console
);
256 ASSERT((Buffer
!= NULL
) || (Buffer
== NULL
&& NumCharsToRead
== 0));
258 /* Call the line-discipline */
259 return TermReadStream(Console
,
269 ConDrvGetConsoleInput(IN PCONSOLE Console
,
270 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
271 IN BOOLEAN KeepEvents
,
272 IN BOOLEAN WaitForMoreEvents
,
273 OUT PINPUT_RECORD InputRecord
,
274 IN ULONG NumEventsToRead
,
275 OUT PULONG NumEventsRead OPTIONAL
)
277 PLIST_ENTRY CurrentInput
;
281 if (Console
== NULL
|| InputBuffer
== NULL
/* || InputRecord == NULL */)
282 return STATUS_INVALID_PARAMETER
;
284 /* Validity checks */
285 ASSERT(Console
== InputBuffer
->Header
.Console
);
286 ASSERT((InputRecord
!= NULL
) || (InputRecord
== NULL
&& NumEventsToRead
== 0));
288 if (NumEventsRead
) *NumEventsRead
= 0;
290 if (IsListEmpty(&InputBuffer
->InputEvents
))
293 * No input is available. Wait for more input if requested,
294 * otherwise, we don't wait, so we return success.
296 return (WaitForMoreEvents
? STATUS_PENDING
: STATUS_SUCCESS
);
299 /* Only get input if there is any */
300 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
302 while ((CurrentInput
!= &InputBuffer
->InputEvents
) && (i
< NumEventsToRead
))
304 Input
= CONTAINING_RECORD(CurrentInput
, ConsoleInput
, ListEntry
);
306 *InputRecord
= Input
->InputEvent
;
310 CurrentInput
= CurrentInput
->Flink
;
312 /* Remove the events from the queue if needed */
315 RemoveEntryList(&Input
->ListEntry
);
316 ConsoleFreeHeap(Input
);
320 if (NumEventsRead
) *NumEventsRead
= i
;
322 if (IsListEmpty(&InputBuffer
->InputEvents
))
324 ResetEvent(InputBuffer
->ActiveEvent
);
327 // FIXME: If we add back UNICODE support, it's here that we need to do the translation.
329 /* We read all the inputs available, we return success */
330 return STATUS_SUCCESS
;
334 ConDrvWriteConsoleInput(IN PCONSOLE Console
,
335 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
336 IN BOOLEAN AppendToEnd
,
337 IN PINPUT_RECORD InputRecord
,
338 IN ULONG NumEventsToWrite
,
339 OUT PULONG NumEventsWritten OPTIONAL
)
341 if (Console
== NULL
|| InputBuffer
== NULL
/* || InputRecord == NULL */)
342 return STATUS_INVALID_PARAMETER
;
344 /* Validity checks */
345 ASSERT(Console
== InputBuffer
->Header
.Console
);
346 ASSERT((InputRecord
!= NULL
) || (InputRecord
== NULL
&& NumEventsToWrite
== 0));
348 /* Now, add the events */
349 if (NumEventsWritten
) *NumEventsWritten
= 0;
351 // FIXME: If we add back UNICODE support, it's here that we need to do the translation.
353 return AddInputEvents(Console
,
361 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console
,
362 IN PCONSOLE_INPUT_BUFFER InputBuffer
)
364 PLIST_ENTRY CurrentEntry
;
367 if (Console
== NULL
|| InputBuffer
== NULL
)
368 return STATUS_INVALID_PARAMETER
;
371 ASSERT(Console
== InputBuffer
->Header
.Console
);
373 /* Discard all entries in the input event queue */
374 while (!IsListEmpty(&InputBuffer
->InputEvents
))
376 CurrentEntry
= RemoveHeadList(&InputBuffer
->InputEvents
);
377 Event
= CONTAINING_RECORD(CurrentEntry
, ConsoleInput
, ListEntry
);
378 ConsoleFreeHeap(Event
);
380 ResetEvent(InputBuffer
->ActiveEvent
);
382 return STATUS_SUCCESS
;
386 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console
,
387 IN PCONSOLE_INPUT_BUFFER InputBuffer
,
388 OUT PULONG NumberOfEvents
)
390 PLIST_ENTRY CurrentInput
;
392 if (Console
== NULL
|| InputBuffer
== NULL
|| NumberOfEvents
== NULL
)
393 return STATUS_INVALID_PARAMETER
;
396 ASSERT(Console
== InputBuffer
->Header
.Console
);
400 /* If there are any events ... */
401 CurrentInput
= InputBuffer
->InputEvents
.Flink
;
402 while (CurrentInput
!= &InputBuffer
->InputEvents
)
404 CurrentInput
= CurrentInput
->Flink
;
408 return STATUS_SUCCESS
;