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 /* PRIVATE FUNCTIONS **********************************************************/
18
19 // ConDrvAddInputEvents
20 static NTSTATUS
AddInputEvents(PCONSOLE Console,PINPUT_RECORD InputRecords,ULONG NumEventsToWrite,PULONG NumEventsWritten,BOOLEAN AppendToEnd)21 AddInputEvents(PCONSOLE Console,
22 PINPUT_RECORD InputRecords, // InputEvent
23 ULONG NumEventsToWrite,
24 PULONG NumEventsWritten,
25 BOOLEAN AppendToEnd)
26 {
27 NTSTATUS Status = STATUS_SUCCESS;
28 ULONG i = 0;
29 BOOLEAN SetWaitEvent = FALSE;
30
31 if (NumEventsWritten) *NumEventsWritten = 0;
32
33 /*
34 * When adding many single events, in the case of repeated mouse move or
35 * key down events, we try to coalesce them so that we do not saturate
36 * too quickly the input buffer.
37 */
38 if (NumEventsToWrite == 1 && !IsListEmpty(&Console->InputBuffer.InputEvents))
39 {
40 PINPUT_RECORD InputRecord = InputRecords; // Only one element
41 PINPUT_RECORD LastInputRecord;
42 ConsoleInput* ConInRec; // Input
43
44 /* Get the "next" event of the input buffer */
45 if (AppendToEnd)
46 {
47 /* Get the tail element */
48 ConInRec = CONTAINING_RECORD(Console->InputBuffer.InputEvents.Blink,
49 ConsoleInput, ListEntry);
50 }
51 else
52 {
53 /* Get the head element */
54 ConInRec = CONTAINING_RECORD(Console->InputBuffer.InputEvents.Flink,
55 ConsoleInput, ListEntry);
56 }
57 LastInputRecord = &ConInRec->InputEvent;
58
59 if (InputRecord->EventType == MOUSE_EVENT &&
60 InputRecord->Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
61 {
62 if (LastInputRecord->EventType == MOUSE_EVENT &&
63 LastInputRecord->Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
64 {
65 /* Update the mouse position */
66 LastInputRecord->Event.MouseEvent.dwMousePosition.X =
67 InputRecord->Event.MouseEvent.dwMousePosition.X;
68 LastInputRecord->Event.MouseEvent.dwMousePosition.Y =
69 InputRecord->Event.MouseEvent.dwMousePosition.Y;
70
71 i = 1;
72 // return STATUS_SUCCESS;
73 Status = STATUS_SUCCESS;
74 }
75 }
76 else if (InputRecord->EventType == KEY_EVENT &&
77 InputRecord->Event.KeyEvent.bKeyDown)
78 {
79 if (LastInputRecord->EventType == KEY_EVENT &&
80 LastInputRecord->Event.KeyEvent.bKeyDown &&
81 (LastInputRecord->Event.KeyEvent.wVirtualScanCode == // Same scancode
82 InputRecord->Event.KeyEvent.wVirtualScanCode) &&
83 (LastInputRecord->Event.KeyEvent.uChar.UnicodeChar == // Same character
84 InputRecord->Event.KeyEvent.uChar.UnicodeChar) &&
85 (LastInputRecord->Event.KeyEvent.dwControlKeyState == // Same Ctrl/Alt/Shift state
86 InputRecord->Event.KeyEvent.dwControlKeyState) )
87 {
88 /* Update the repeat count */
89 LastInputRecord->Event.KeyEvent.wRepeatCount +=
90 InputRecord->Event.KeyEvent.wRepeatCount;
91
92 i = 1;
93 // return STATUS_SUCCESS;
94 Status = STATUS_SUCCESS;
95 }
96 }
97 }
98
99 /* If we coalesced the only one element, we can quit */
100 if (i == 1 && Status == STATUS_SUCCESS /* && NumEventsToWrite == 1 */)
101 goto Done;
102
103 /*
104 * No event coalesced, add them in the usual way.
105 */
106
107 if (AppendToEnd)
108 {
109 /* Go to the beginning of the list */
110 // InputRecords = InputRecords;
111 }
112 else
113 {
114 /* Go to the end of the list */
115 InputRecords = &InputRecords[NumEventsToWrite - 1];
116 }
117
118 /* Set the event if the list is going to be non-empty */
119 if (IsListEmpty(&Console->InputBuffer.InputEvents))
120 SetWaitEvent = TRUE;
121
122 for (i = 0; i < NumEventsToWrite && NT_SUCCESS(Status); ++i)
123 {
124 PINPUT_RECORD InputRecord;
125 ConsoleInput* ConInRec;
126
127 if (AppendToEnd)
128 {
129 /* Select the event and go to the next one */
130 InputRecord = InputRecords++;
131 }
132 else
133 {
134 /* Select the event and go to the previous one */
135 InputRecord = InputRecords--;
136 }
137
138 /* Add event to the queue */
139 ConInRec = ConsoleAllocHeap(0, sizeof(ConsoleInput));
140 if (ConInRec == NULL)
141 {
142 // return STATUS_INSUFFICIENT_RESOURCES;
143 Status = STATUS_INSUFFICIENT_RESOURCES;
144 continue;
145 }
146
147 ConInRec->InputEvent = *InputRecord;
148
149 if (AppendToEnd)
150 {
151 /* Append the event to the end of the queue */
152 InsertTailList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry);
153 }
154 else
155 {
156 /* Append the event to the beginning of the queue */
157 InsertHeadList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry);
158 }
159 _InterlockedIncrement((PLONG)&Console->InputBuffer.NumberOfEvents);
160
161 // return STATUS_SUCCESS;
162 Status = STATUS_SUCCESS;
163 }
164
165 if (SetWaitEvent) NtSetEvent(Console->InputBuffer.ActiveEvent, NULL);
166
167 Done:
168 if (NumEventsWritten) *NumEventsWritten = i;
169
170 return Status;
171 }
172
173 static VOID
PurgeInputBuffer(IN PCONSOLE_INPUT_BUFFER InputBuffer)174 PurgeInputBuffer(IN PCONSOLE_INPUT_BUFFER InputBuffer)
175 {
176 PLIST_ENTRY CurrentEntry;
177 ConsoleInput* Event;
178
179 /* Discard all entries in the input event queue */
180 _InterlockedExchange((PLONG)&InputBuffer->NumberOfEvents, 0);
181 while (!IsListEmpty(&InputBuffer->InputEvents))
182 {
183 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
184 Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
185 ConsoleFreeHeap(Event);
186 }
187
188 // NtClose(Console->InputBuffer.ActiveEvent);
189 }
190
191 NTSTATUS NTAPI
ConDrvInitInputBuffer(IN PCONSOLE Console,IN ULONG InputBufferSize)192 ConDrvInitInputBuffer(IN PCONSOLE Console,
193 IN ULONG InputBufferSize)
194 {
195 NTSTATUS Status;
196 OBJECT_ATTRIBUTES ObjectAttributes;
197
198 ConSrvInitObject(&Console->InputBuffer.Header, INPUT_BUFFER, Console);
199
200 InitializeObjectAttributes(&ObjectAttributes,
201 NULL,
202 OBJ_INHERIT,
203 NULL,
204 NULL);
205
206 Status = NtCreateEvent(&Console->InputBuffer.ActiveEvent, EVENT_ALL_ACCESS,
207 &ObjectAttributes, NotificationEvent, FALSE);
208 if (!NT_SUCCESS(Status))
209 return Status;
210
211 Console->InputBuffer.InputBufferSize = InputBufferSize;
212 Console->InputBuffer.NumberOfEvents = 0;
213 InitializeListHead(&Console->InputBuffer.InputEvents);
214 Console->InputBuffer.Mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
215 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT;
216
217 return STATUS_SUCCESS;
218 }
219
220 VOID NTAPI
ConDrvDeinitInputBuffer(IN PCONSOLE Console)221 ConDrvDeinitInputBuffer(IN PCONSOLE Console)
222 {
223 PurgeInputBuffer(&Console->InputBuffer);
224 NtClose(Console->InputBuffer.ActiveEvent);
225 }
226
227
228 /* PUBLIC DRIVER APIS *********************************************************/
229
230 NTSTATUS NTAPI
ConDrvReadConsole(IN PCONSOLE Console,IN PCONSOLE_INPUT_BUFFER InputBuffer,IN BOOLEAN Unicode,OUT PVOID Buffer,IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,IN PVOID Parameter OPTIONAL,IN ULONG NumCharsToRead,OUT PULONG NumCharsRead OPTIONAL)231 ConDrvReadConsole(IN PCONSOLE Console,
232 IN PCONSOLE_INPUT_BUFFER InputBuffer,
233 IN BOOLEAN Unicode,
234 OUT PVOID Buffer,
235 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
236 IN PVOID Parameter OPTIONAL,
237 IN ULONG NumCharsToRead,
238 OUT PULONG NumCharsRead OPTIONAL)
239 {
240 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
241 // NTSTATUS Status; = STATUS_PENDING;
242
243 if (Console == NULL || InputBuffer == NULL || /* Buffer == NULL || */
244 ReadControl == NULL || ReadControl->nLength != sizeof(CONSOLE_READCONSOLE_CONTROL))
245 {
246 return STATUS_INVALID_PARAMETER;
247 }
248
249 /* Validity checks */
250 ASSERT(Console == InputBuffer->Header.Console);
251 ASSERT((Buffer != NULL) || (Buffer == NULL && NumCharsToRead == 0));
252
253 /* Call the line-discipline */
254 return TermReadStream(Console,
255 Unicode,
256 Buffer,
257 ReadControl,
258 Parameter,
259 NumCharsToRead,
260 NumCharsRead);
261 }
262
263 NTSTATUS NTAPI
ConDrvGetConsoleInput(IN PCONSOLE Console,IN PCONSOLE_INPUT_BUFFER InputBuffer,IN BOOLEAN KeepEvents,IN BOOLEAN WaitForMoreEvents,OUT PINPUT_RECORD InputRecord,IN ULONG NumEventsToRead,OUT PULONG NumEventsRead OPTIONAL)264 ConDrvGetConsoleInput(IN PCONSOLE Console,
265 IN PCONSOLE_INPUT_BUFFER InputBuffer,
266 IN BOOLEAN KeepEvents,
267 IN BOOLEAN WaitForMoreEvents,
268 OUT PINPUT_RECORD InputRecord,
269 IN ULONG NumEventsToRead,
270 OUT PULONG NumEventsRead OPTIONAL)
271 {
272 PLIST_ENTRY CurrentInput;
273 ConsoleInput* Input;
274 ULONG i = 0;
275
276 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */)
277 return STATUS_INVALID_PARAMETER;
278
279 /* Validity checks */
280 ASSERT(Console == InputBuffer->Header.Console);
281 ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToRead == 0));
282
283 if (NumEventsRead) *NumEventsRead = 0;
284
285 if (IsListEmpty(&InputBuffer->InputEvents))
286 {
287 /*
288 * No input is available. Wait for more input if requested,
289 * otherwise, we don't wait, so we return success.
290 */
291 return (WaitForMoreEvents ? STATUS_PENDING : STATUS_SUCCESS);
292 }
293
294 /* Only get input if there is any */
295 CurrentInput = InputBuffer->InputEvents.Flink;
296 i = 0;
297 while ((CurrentInput != &InputBuffer->InputEvents) && (i < NumEventsToRead))
298 {
299 Input = CONTAINING_RECORD(CurrentInput, ConsoleInput, ListEntry);
300
301 *InputRecord = Input->InputEvent;
302
303 ++InputRecord;
304 ++i;
305 CurrentInput = CurrentInput->Flink;
306
307 /* Remove the events from the queue if needed */
308 if (!KeepEvents)
309 {
310 _InterlockedDecrement((PLONG)&InputBuffer->NumberOfEvents);
311 RemoveEntryList(&Input->ListEntry);
312 ConsoleFreeHeap(Input);
313 }
314 }
315
316 if (NumEventsRead) *NumEventsRead = i;
317
318 if (IsListEmpty(&InputBuffer->InputEvents))
319 {
320 NtClearEvent(InputBuffer->ActiveEvent);
321 }
322
323 // FIXME: If we add back UNICODE support, it's here that we need to do the translation.
324
325 /* We read all the inputs available, we return success */
326 return STATUS_SUCCESS;
327 }
328
329 NTSTATUS NTAPI
ConDrvWriteConsoleInput(IN PCONSOLE Console,IN PCONSOLE_INPUT_BUFFER InputBuffer,IN BOOLEAN AppendToEnd,IN PINPUT_RECORD InputRecord,IN ULONG NumEventsToWrite,OUT PULONG NumEventsWritten OPTIONAL)330 ConDrvWriteConsoleInput(IN PCONSOLE Console,
331 IN PCONSOLE_INPUT_BUFFER InputBuffer,
332 IN BOOLEAN AppendToEnd,
333 IN PINPUT_RECORD InputRecord,
334 IN ULONG NumEventsToWrite,
335 OUT PULONG NumEventsWritten OPTIONAL)
336 {
337 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */)
338 return STATUS_INVALID_PARAMETER;
339
340 /* Validity checks */
341 ASSERT(Console == InputBuffer->Header.Console);
342 ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToWrite == 0));
343
344 /* Now, add the events */
345 if (NumEventsWritten) *NumEventsWritten = 0;
346
347 // FIXME: If we add back UNICODE support, it's here that we need to do the translation.
348
349 return AddInputEvents(Console,
350 InputRecord,
351 NumEventsToWrite,
352 NumEventsWritten,
353 AppendToEnd);
354 }
355
356 NTSTATUS NTAPI
ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,IN PCONSOLE_INPUT_BUFFER InputBuffer)357 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
358 IN PCONSOLE_INPUT_BUFFER InputBuffer)
359 {
360 if (Console == NULL || InputBuffer == NULL)
361 return STATUS_INVALID_PARAMETER;
362
363 /* Validity check */
364 ASSERT(Console == InputBuffer->Header.Console);
365
366 /* Discard all entries in the input event queue */
367 PurgeInputBuffer(InputBuffer);
368 NtClearEvent(InputBuffer->ActiveEvent);
369
370 return STATUS_SUCCESS;
371 }
372
373 NTSTATUS NTAPI
ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,IN PCONSOLE_INPUT_BUFFER InputBuffer,OUT PULONG NumberOfEvents)374 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
375 IN PCONSOLE_INPUT_BUFFER InputBuffer,
376 OUT PULONG NumberOfEvents)
377 {
378 if (Console == NULL || InputBuffer == NULL || NumberOfEvents == NULL)
379 return STATUS_INVALID_PARAMETER;
380
381 /* Validity check */
382 ASSERT(Console == InputBuffer->Header.Console);
383
384 *NumberOfEvents = InputBuffer->NumberOfEvents;
385 return STATUS_SUCCESS;
386 }
387
388 /* EOF */
389