* Sync with trunk r64401.
[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) SetEvent(Console->InputBuffer.ActiveEvent);
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 SECURITY_ATTRIBUTES SecurityAttributes;
202
203 ConSrvInitObject(&Console->InputBuffer.Header, INPUT_BUFFER, Console);
204
205 SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
206 SecurityAttributes.lpSecurityDescriptor = NULL;
207 SecurityAttributes.bInheritHandle = TRUE;
208
209 Console->InputBuffer.ActiveEvent = CreateEventW(&SecurityAttributes, TRUE, FALSE, NULL);
210 if (Console->InputBuffer.ActiveEvent == NULL) return STATUS_UNSUCCESSFUL;
211
212 Console->InputBuffer.InputBufferSize = InputBufferSize;
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
221 ConDrvDeinitInputBuffer(IN PCONSOLE Console)
222 {
223 PurgeInputBuffer(Console);
224 CloseHandle(Console->InputBuffer.ActiveEvent);
225 }
226
227
228 /* PUBLIC DRIVER APIS *********************************************************/
229
230 NTSTATUS NTAPI
231 ConDrvReadConsole(IN PCONSOLE Console,
232 IN PCONSOLE_INPUT_BUFFER InputBuffer,
233 /**/IN PUNICODE_STRING ExeName /**/OPTIONAL/**/,/**/
234 IN BOOLEAN Unicode,
235 OUT PVOID Buffer,
236 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
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 ExeName,
256 Unicode,
257 Buffer,
258 ReadControl,
259 NumCharsToRead,
260 NumCharsRead);
261 }
262
263 NTSTATUS NTAPI
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 RemoveEntryList(&Input->ListEntry);
311 ConsoleFreeHeap(Input);
312 }
313 }
314
315 if (NumEventsRead) *NumEventsRead = i;
316
317 if (IsListEmpty(&InputBuffer->InputEvents))
318 {
319 ResetEvent(InputBuffer->ActiveEvent);
320 }
321
322 // FIXME: If we add back UNICODE support, it's here that we need to do the translation.
323
324 /* We read all the inputs available, we return success */
325 return STATUS_SUCCESS;
326 }
327
328 NTSTATUS NTAPI
329 ConDrvWriteConsoleInput(IN PCONSOLE Console,
330 IN PCONSOLE_INPUT_BUFFER InputBuffer,
331 IN BOOLEAN AppendToEnd,
332 IN PINPUT_RECORD InputRecord,
333 IN ULONG NumEventsToWrite,
334 OUT PULONG NumEventsWritten OPTIONAL)
335 {
336 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */)
337 return STATUS_INVALID_PARAMETER;
338
339 /* Validity checks */
340 ASSERT(Console == InputBuffer->Header.Console);
341 ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToWrite == 0));
342
343 /* Now, add the events */
344 if (NumEventsWritten) *NumEventsWritten = 0;
345
346 // FIXME: If we add back UNICODE support, it's here that we need to do the translation.
347
348 return AddInputEvents(Console,
349 InputRecord,
350 NumEventsToWrite,
351 NumEventsWritten,
352 AppendToEnd);
353 }
354
355 NTSTATUS NTAPI
356 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
357 IN PCONSOLE_INPUT_BUFFER InputBuffer)
358 {
359 PLIST_ENTRY CurrentEntry;
360 ConsoleInput* Event;
361
362 if (Console == NULL || InputBuffer == NULL)
363 return STATUS_INVALID_PARAMETER;
364
365 /* Validity check */
366 ASSERT(Console == InputBuffer->Header.Console);
367
368 /* Discard all entries in the input event queue */
369 while (!IsListEmpty(&InputBuffer->InputEvents))
370 {
371 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
372 Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
373 ConsoleFreeHeap(Event);
374 }
375 ResetEvent(InputBuffer->ActiveEvent);
376
377 return STATUS_SUCCESS;
378 }
379
380 NTSTATUS NTAPI
381 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
382 IN PCONSOLE_INPUT_BUFFER InputBuffer,
383 OUT PULONG NumberOfEvents)
384 {
385 PLIST_ENTRY CurrentInput;
386
387 if (Console == NULL || InputBuffer == NULL || NumberOfEvents == NULL)
388 return STATUS_INVALID_PARAMETER;
389
390 /* Validity check */
391 ASSERT(Console == InputBuffer->Header.Console);
392
393 *NumberOfEvents = 0;
394
395 /* If there are any events ... */
396 CurrentInput = InputBuffer->InputEvents.Flink;
397 while (CurrentInput != &InputBuffer->InputEvents)
398 {
399 CurrentInput = CurrentInput->Flink;
400 (*NumberOfEvents)++;
401 }
402
403 return STATUS_SUCCESS;
404 }
405
406 /* EOF */