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