e3894d8a54c8f42f33c0623055ab581cf5b1b8a0
[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 /* PRIVATE FUNCTIONS **********************************************************/
18
19 // ConDrvAddInputEvents
20 static NTSTATUS
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
160 // return STATUS_SUCCESS;
161 Status = STATUS_SUCCESS;
162 }
163
164 if (SetWaitEvent) NtSetEvent(Console->InputBuffer.ActiveEvent, NULL);
165
166 Done:
167 if (NumEventsWritten) *NumEventsWritten = i;
168
169 return Status;
170 }
171
172 static VOID
173 PurgeInputBuffer(PCONSOLE Console)
174 {
175 PLIST_ENTRY CurrentEntry;
176 ConsoleInput* Event;
177
178 while (!IsListEmpty(&Console->InputBuffer.InputEvents))
179 {
180 CurrentEntry = RemoveHeadList(&Console->InputBuffer.InputEvents);
181 Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
182 ConsoleFreeHeap(Event);
183 }
184
185 // CloseHandle(Console->InputBuffer.ActiveEvent);
186 }
187
188 NTSTATUS NTAPI
189 ConDrvInitInputBuffer(IN PCONSOLE Console,
190 IN ULONG InputBufferSize)
191 {
192 NTSTATUS Status;
193 OBJECT_ATTRIBUTES ObjectAttributes;
194
195 ConSrvInitObject(&Console->InputBuffer.Header, INPUT_BUFFER, Console);
196
197 InitializeObjectAttributes(&ObjectAttributes,
198 NULL,
199 OBJ_INHERIT,
200 NULL,
201 NULL);
202
203 Status = NtCreateEvent(&Console->InputBuffer.ActiveEvent, EVENT_ALL_ACCESS,
204 &ObjectAttributes, NotificationEvent, FALSE);
205 if (!NT_SUCCESS(Status))
206 return Status;
207
208 Console->InputBuffer.InputBufferSize = InputBufferSize;
209 InitializeListHead(&Console->InputBuffer.InputEvents);
210 Console->InputBuffer.Mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
211 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT;
212
213 return STATUS_SUCCESS;
214 }
215
216 VOID NTAPI
217 ConDrvDeinitInputBuffer(IN PCONSOLE Console)
218 {
219 PurgeInputBuffer(Console);
220 CloseHandle(Console->InputBuffer.ActiveEvent);
221 }
222
223
224 /* PUBLIC DRIVER APIS *********************************************************/
225
226 NTSTATUS NTAPI
227 ConDrvReadConsole(IN PCONSOLE Console,
228 IN PCONSOLE_INPUT_BUFFER InputBuffer,
229 IN BOOLEAN Unicode,
230 OUT PVOID Buffer,
231 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
232 IN PVOID Parameter OPTIONAL,
233 IN ULONG NumCharsToRead,
234 OUT PULONG NumCharsRead OPTIONAL)
235 {
236 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
237 // NTSTATUS Status; = STATUS_PENDING;
238
239 if (Console == NULL || InputBuffer == NULL || /* Buffer == NULL || */
240 ReadControl == NULL || ReadControl->nLength != sizeof(CONSOLE_READCONSOLE_CONTROL))
241 {
242 return STATUS_INVALID_PARAMETER;
243 }
244
245 /* Validity checks */
246 ASSERT(Console == InputBuffer->Header.Console);
247 ASSERT((Buffer != NULL) || (Buffer == NULL && NumCharsToRead == 0));
248
249 /* Call the line-discipline */
250 return TermReadStream(Console,
251 Unicode,
252 Buffer,
253 ReadControl,
254 Parameter,
255 NumCharsToRead,
256 NumCharsRead);
257 }
258
259 NTSTATUS NTAPI
260 ConDrvGetConsoleInput(IN PCONSOLE Console,
261 IN PCONSOLE_INPUT_BUFFER InputBuffer,
262 IN BOOLEAN KeepEvents,
263 IN BOOLEAN WaitForMoreEvents,
264 OUT PINPUT_RECORD InputRecord,
265 IN ULONG NumEventsToRead,
266 OUT PULONG NumEventsRead OPTIONAL)
267 {
268 PLIST_ENTRY CurrentInput;
269 ConsoleInput* Input;
270 ULONG i = 0;
271
272 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */)
273 return STATUS_INVALID_PARAMETER;
274
275 /* Validity checks */
276 ASSERT(Console == InputBuffer->Header.Console);
277 ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToRead == 0));
278
279 if (NumEventsRead) *NumEventsRead = 0;
280
281 if (IsListEmpty(&InputBuffer->InputEvents))
282 {
283 /*
284 * No input is available. Wait for more input if requested,
285 * otherwise, we don't wait, so we return success.
286 */
287 return (WaitForMoreEvents ? STATUS_PENDING : STATUS_SUCCESS);
288 }
289
290 /* Only get input if there is any */
291 CurrentInput = InputBuffer->InputEvents.Flink;
292 i = 0;
293 while ((CurrentInput != &InputBuffer->InputEvents) && (i < NumEventsToRead))
294 {
295 Input = CONTAINING_RECORD(CurrentInput, ConsoleInput, ListEntry);
296
297 *InputRecord = Input->InputEvent;
298
299 ++InputRecord;
300 ++i;
301 CurrentInput = CurrentInput->Flink;
302
303 /* Remove the events from the queue if needed */
304 if (!KeepEvents)
305 {
306 RemoveEntryList(&Input->ListEntry);
307 ConsoleFreeHeap(Input);
308 }
309 }
310
311 if (NumEventsRead) *NumEventsRead = i;
312
313 if (IsListEmpty(&InputBuffer->InputEvents))
314 {
315 ResetEvent(InputBuffer->ActiveEvent);
316 }
317
318 // FIXME: If we add back UNICODE support, it's here that we need to do the translation.
319
320 /* We read all the inputs available, we return success */
321 return STATUS_SUCCESS;
322 }
323
324 NTSTATUS NTAPI
325 ConDrvWriteConsoleInput(IN PCONSOLE Console,
326 IN PCONSOLE_INPUT_BUFFER InputBuffer,
327 IN BOOLEAN AppendToEnd,
328 IN PINPUT_RECORD InputRecord,
329 IN ULONG NumEventsToWrite,
330 OUT PULONG NumEventsWritten OPTIONAL)
331 {
332 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */)
333 return STATUS_INVALID_PARAMETER;
334
335 /* Validity checks */
336 ASSERT(Console == InputBuffer->Header.Console);
337 ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToWrite == 0));
338
339 /* Now, add the events */
340 if (NumEventsWritten) *NumEventsWritten = 0;
341
342 // FIXME: If we add back UNICODE support, it's here that we need to do the translation.
343
344 return AddInputEvents(Console,
345 InputRecord,
346 NumEventsToWrite,
347 NumEventsWritten,
348 AppendToEnd);
349 }
350
351 NTSTATUS NTAPI
352 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
353 IN PCONSOLE_INPUT_BUFFER InputBuffer)
354 {
355 PLIST_ENTRY CurrentEntry;
356 ConsoleInput* Event;
357
358 if (Console == NULL || InputBuffer == NULL)
359 return STATUS_INVALID_PARAMETER;
360
361 /* Validity check */
362 ASSERT(Console == InputBuffer->Header.Console);
363
364 /* Discard all entries in the input event queue */
365 while (!IsListEmpty(&InputBuffer->InputEvents))
366 {
367 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
368 Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
369 ConsoleFreeHeap(Event);
370 }
371 ResetEvent(InputBuffer->ActiveEvent);
372
373 return STATUS_SUCCESS;
374 }
375
376 NTSTATUS NTAPI
377 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
378 IN PCONSOLE_INPUT_BUFFER InputBuffer,
379 OUT PULONG NumberOfEvents)
380 {
381 PLIST_ENTRY CurrentInput;
382
383 if (Console == NULL || InputBuffer == NULL || NumberOfEvents == NULL)
384 return STATUS_INVALID_PARAMETER;
385
386 /* Validity check */
387 ASSERT(Console == InputBuffer->Header.Console);
388
389 *NumberOfEvents = 0;
390
391 /* If there are any events ... */
392 CurrentInput = InputBuffer->InputEvents.Flink;
393 while (CurrentInput != &InputBuffer->InputEvents)
394 {
395 CurrentInput = CurrentInput->Flink;
396 (*NumberOfEvents)++;
397 }
398
399 return STATUS_SUCCESS;
400 }
401
402 /* EOF */