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