Create the AHCI branch for Aman's work
[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 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 return Status;
216
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;
221
222 return STATUS_SUCCESS;
223 }
224
225 VOID NTAPI
226 ConDrvDeinitInputBuffer(IN PCONSOLE Console)
227 {
228 PurgeInputBuffer(Console);
229 CloseHandle(Console->InputBuffer.ActiveEvent);
230 }
231
232
233 /* PUBLIC DRIVER APIS *********************************************************/
234
235 NTSTATUS NTAPI
236 ConDrvReadConsole(IN PCONSOLE Console,
237 IN PCONSOLE_INPUT_BUFFER InputBuffer,
238 IN BOOLEAN Unicode,
239 OUT PVOID Buffer,
240 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
241 IN PVOID Parameter OPTIONAL,
242 IN ULONG NumCharsToRead,
243 OUT PULONG NumCharsRead OPTIONAL)
244 {
245 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
246 // NTSTATUS Status; = STATUS_PENDING;
247
248 if (Console == NULL || InputBuffer == NULL || /* Buffer == NULL || */
249 ReadControl == NULL || ReadControl->nLength != sizeof(CONSOLE_READCONSOLE_CONTROL))
250 {
251 return STATUS_INVALID_PARAMETER;
252 }
253
254 /* Validity checks */
255 ASSERT(Console == InputBuffer->Header.Console);
256 ASSERT((Buffer != NULL) || (Buffer == NULL && NumCharsToRead == 0));
257
258 /* Call the line-discipline */
259 return TermReadStream(Console,
260 Unicode,
261 Buffer,
262 ReadControl,
263 Parameter,
264 NumCharsToRead,
265 NumCharsRead);
266 }
267
268 NTSTATUS NTAPI
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)
276 {
277 PLIST_ENTRY CurrentInput;
278 ConsoleInput* Input;
279 ULONG i = 0;
280
281 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */)
282 return STATUS_INVALID_PARAMETER;
283
284 /* Validity checks */
285 ASSERT(Console == InputBuffer->Header.Console);
286 ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToRead == 0));
287
288 if (NumEventsRead) *NumEventsRead = 0;
289
290 if (IsListEmpty(&InputBuffer->InputEvents))
291 {
292 /*
293 * No input is available. Wait for more input if requested,
294 * otherwise, we don't wait, so we return success.
295 */
296 return (WaitForMoreEvents ? STATUS_PENDING : STATUS_SUCCESS);
297 }
298
299 /* Only get input if there is any */
300 CurrentInput = InputBuffer->InputEvents.Flink;
301 i = 0;
302 while ((CurrentInput != &InputBuffer->InputEvents) && (i < NumEventsToRead))
303 {
304 Input = CONTAINING_RECORD(CurrentInput, ConsoleInput, ListEntry);
305
306 *InputRecord = Input->InputEvent;
307
308 ++InputRecord;
309 ++i;
310 CurrentInput = CurrentInput->Flink;
311
312 /* Remove the events from the queue if needed */
313 if (!KeepEvents)
314 {
315 RemoveEntryList(&Input->ListEntry);
316 ConsoleFreeHeap(Input);
317 }
318 }
319
320 if (NumEventsRead) *NumEventsRead = i;
321
322 if (IsListEmpty(&InputBuffer->InputEvents))
323 {
324 ResetEvent(InputBuffer->ActiveEvent);
325 }
326
327 // FIXME: If we add back UNICODE support, it's here that we need to do the translation.
328
329 /* We read all the inputs available, we return success */
330 return STATUS_SUCCESS;
331 }
332
333 NTSTATUS NTAPI
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)
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 && NumEventsToWrite == 0));
347
348 /* Now, add the events */
349 if (NumEventsWritten) *NumEventsWritten = 0;
350
351 // FIXME: If we add back UNICODE support, it's here that we need to do the translation.
352
353 return AddInputEvents(Console,
354 InputRecord,
355 NumEventsToWrite,
356 NumEventsWritten,
357 AppendToEnd);
358 }
359
360 NTSTATUS NTAPI
361 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
362 IN PCONSOLE_INPUT_BUFFER InputBuffer)
363 {
364 PLIST_ENTRY CurrentEntry;
365 ConsoleInput* Event;
366
367 if (Console == NULL || InputBuffer == NULL)
368 return STATUS_INVALID_PARAMETER;
369
370 /* Validity check */
371 ASSERT(Console == InputBuffer->Header.Console);
372
373 /* Discard all entries in the input event queue */
374 while (!IsListEmpty(&InputBuffer->InputEvents))
375 {
376 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
377 Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
378 ConsoleFreeHeap(Event);
379 }
380 ResetEvent(InputBuffer->ActiveEvent);
381
382 return STATUS_SUCCESS;
383 }
384
385 NTSTATUS NTAPI
386 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
387 IN PCONSOLE_INPUT_BUFFER InputBuffer,
388 OUT PULONG NumberOfEvents)
389 {
390 PLIST_ENTRY CurrentInput;
391
392 if (Console == NULL || InputBuffer == NULL || NumberOfEvents == NULL)
393 return STATUS_INVALID_PARAMETER;
394
395 /* Validity check */
396 ASSERT(Console == InputBuffer->Header.Console);
397
398 *NumberOfEvents = 0;
399
400 /* If there are any events ... */
401 CurrentInput = InputBuffer->InputEvents.Flink;
402 while (CurrentInput != &InputBuffer->InputEvents)
403 {
404 CurrentInput = CurrentInput->Flink;
405 (*NumberOfEvents)++;
406 }
407
408 return STATUS_SUCCESS;
409 }
410
411 /* EOF */