c7b5e27b765970daa876ddf7f58f4a02ae476535
[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 #define ConsoleInputUnicodeCharToAnsiChar(Console, dChar, sWChar) \
20 WideCharToMultiByte((Console)->InputCodePage, 0, (sWChar), 1, (dChar), 1, NULL, NULL)
21
22 #define ConsoleInputAnsiCharToUnicodeChar(Console, dWChar, sChar) \
23 MultiByteToWideChar((Console)->InputCodePage, 0, (sChar), 1, (dWChar), 1)
24
25 typedef struct ConsoleInput_t
26 {
27 LIST_ENTRY ListEntry;
28 INPUT_RECORD InputEvent;
29 } ConsoleInput;
30
31
32 /* PRIVATE FUNCTIONS **********************************************************/
33
34 static VOID
35 ConioInputEventToAnsi(PCONSOLE Console, PINPUT_RECORD InputEvent)
36 {
37 if (InputEvent->EventType == KEY_EVENT)
38 {
39 WCHAR UnicodeChar = InputEvent->Event.KeyEvent.uChar.UnicodeChar;
40 InputEvent->Event.KeyEvent.uChar.UnicodeChar = 0;
41 ConsoleInputUnicodeCharToAnsiChar(Console,
42 &InputEvent->Event.KeyEvent.uChar.AsciiChar,
43 &UnicodeChar);
44 }
45 }
46
47 static VOID
48 ConioInputEventToUnicode(PCONSOLE Console, PINPUT_RECORD InputEvent)
49 {
50 if (InputEvent->EventType == KEY_EVENT)
51 {
52 CHAR AsciiChar = InputEvent->Event.KeyEvent.uChar.AsciiChar;
53 InputEvent->Event.KeyEvent.uChar.AsciiChar = 0;
54 ConsoleInputAnsiCharToUnicodeChar(Console,
55 &InputEvent->Event.KeyEvent.uChar.UnicodeChar,
56 &AsciiChar);
57 }
58 }
59
60 NTSTATUS
61 ConioAddInputEvent(PCONSOLE Console,
62 PINPUT_RECORD InputEvent,
63 BOOLEAN AppendToEnd)
64 {
65 ConsoleInput *ConInRec;
66
67 /* Check for pause or unpause */
68 if (InputEvent->EventType == KEY_EVENT && InputEvent->Event.KeyEvent.bKeyDown)
69 {
70 WORD vk = InputEvent->Event.KeyEvent.wVirtualKeyCode;
71 if (!(Console->PauseFlags & PAUSED_FROM_KEYBOARD))
72 {
73 DWORD cks = InputEvent->Event.KeyEvent.dwControlKeyState;
74 if (Console->InputBuffer.Mode & ENABLE_LINE_INPUT &&
75 (vk == VK_PAUSE || (vk == 'S' &&
76 (cks & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) &&
77 !(cks & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)))))
78 {
79 ConioPause(Console, PAUSED_FROM_KEYBOARD);
80 return STATUS_SUCCESS;
81 }
82 }
83 else
84 {
85 if ((vk < VK_SHIFT || vk > VK_CAPITAL) && vk != VK_LWIN &&
86 vk != VK_RWIN && vk != VK_NUMLOCK && vk != VK_SCROLL)
87 {
88 ConioUnpause(Console, PAUSED_FROM_KEYBOARD);
89 return STATUS_SUCCESS;
90 }
91 }
92 }
93
94 /* Add event to the queue */
95 ConInRec = ConsoleAllocHeap(0, sizeof(ConsoleInput));
96 if (ConInRec == NULL) return STATUS_INSUFFICIENT_RESOURCES;
97
98 ConInRec->InputEvent = *InputEvent;
99
100 if (AppendToEnd)
101 {
102 /* Append the event to the end of the queue */
103 InsertTailList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry);
104 }
105 else
106 {
107 /* Append the event to the beginning of the queue */
108 InsertHeadList(&Console->InputBuffer.InputEvents, &ConInRec->ListEntry);
109 }
110
111 SetEvent(Console->InputBuffer.ActiveEvent);
112 CsrNotifyWait(&Console->ReadWaitQueue,
113 FALSE,
114 NULL,
115 NULL);
116 if (!IsListEmpty(&Console->ReadWaitQueue))
117 {
118 CsrDereferenceWait(&Console->ReadWaitQueue);
119 }
120
121 return STATUS_SUCCESS;
122 }
123
124 NTSTATUS
125 ConioProcessInputEvent(PCONSOLE Console,
126 PINPUT_RECORD InputEvent)
127 {
128 return ConioAddInputEvent(Console, InputEvent, TRUE);
129 }
130
131 VOID
132 PurgeInputBuffer(PCONSOLE Console)
133 {
134 PLIST_ENTRY CurrentEntry;
135 ConsoleInput* Event;
136
137 while (!IsListEmpty(&Console->InputBuffer.InputEvents))
138 {
139 CurrentEntry = RemoveHeadList(&Console->InputBuffer.InputEvents);
140 Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
141 ConsoleFreeHeap(Event);
142 }
143
144 CloseHandle(Console->InputBuffer.ActiveEvent);
145 }
146
147
148 /* PUBLIC DRIVER APIS *********************************************************/
149
150 NTSTATUS NTAPI
151 ConDrvReadConsole(IN PCONSOLE Console,
152 IN PCONSOLE_INPUT_BUFFER InputBuffer,
153 IN BOOLEAN Unicode,
154 OUT PVOID Buffer,
155 IN OUT PCONSOLE_READCONSOLE_CONTROL ReadControl,
156 IN ULONG NumCharsToRead,
157 OUT PULONG NumCharsRead OPTIONAL)
158 {
159 // STATUS_PENDING : Wait if more to read ; STATUS_SUCCESS : Don't wait.
160 NTSTATUS Status = STATUS_PENDING;
161 PLIST_ENTRY CurrentEntry;
162 ConsoleInput *Input;
163 ULONG i;
164
165 if (Console == NULL || InputBuffer == NULL || /* Buffer == NULL || */
166 ReadControl == NULL || ReadControl->nLength != sizeof(CONSOLE_READCONSOLE_CONTROL))
167 {
168 return STATUS_INVALID_PARAMETER;
169 }
170
171 /* Validity checks */
172 ASSERT(Console == InputBuffer->Header.Console);
173 ASSERT((Buffer != NULL) || (Buffer == NULL && NumCharsToRead == 0));
174
175 /* We haven't read anything (yet) */
176
177 i = ReadControl->nInitialChars;
178
179 if (InputBuffer->Mode & ENABLE_LINE_INPUT)
180 {
181 if (Console->LineBuffer == NULL)
182 {
183 /* Starting a new line */
184 Console->LineMaxSize = (WORD)max(256, NumCharsToRead);
185
186 Console->LineBuffer = ConsoleAllocHeap(0, Console->LineMaxSize * sizeof(WCHAR));
187 if (Console->LineBuffer == NULL) return STATUS_NO_MEMORY;
188
189 Console->LineComplete = FALSE;
190 Console->LineUpPressed = FALSE;
191 Console->LineInsertToggle = Console->InsertMode;
192 Console->LineWakeupMask = ReadControl->dwCtrlWakeupMask;
193 Console->LineSize = ReadControl->nInitialChars;
194 Console->LinePos = Console->LineSize;
195
196 /*
197 * Pre-filling the buffer is only allowed in the Unicode API,
198 * so we don't need to worry about ANSI <-> Unicode conversion.
199 */
200 memcpy(Console->LineBuffer, Buffer, Console->LineSize * sizeof(WCHAR));
201 if (Console->LineSize == Console->LineMaxSize)
202 {
203 Console->LineComplete = TRUE;
204 Console->LinePos = 0;
205 }
206 }
207
208 /* If we don't have a complete line yet, process the pending input */
209 while (!Console->LineComplete && !IsListEmpty(&InputBuffer->InputEvents))
210 {
211 /* Remove input event from queue */
212 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
213 if (IsListEmpty(&InputBuffer->InputEvents))
214 {
215 ResetEvent(InputBuffer->ActiveEvent);
216 }
217 Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
218
219 /* Only pay attention to key down */
220 if (Input->InputEvent.EventType == KEY_EVENT &&
221 Input->InputEvent.Event.KeyEvent.bKeyDown)
222 {
223 LineInputKeyDown(Console, &Input->InputEvent.Event.KeyEvent);
224 ReadControl->dwControlKeyState = Input->InputEvent.Event.KeyEvent.dwControlKeyState;
225 }
226 ConsoleFreeHeap(Input);
227 }
228
229 /* Check if we have a complete line to read from */
230 if (Console->LineComplete)
231 {
232 while (i < NumCharsToRead && Console->LinePos != Console->LineSize)
233 {
234 WCHAR Char = Console->LineBuffer[Console->LinePos++];
235
236 if (Unicode)
237 {
238 ((PWCHAR)Buffer)[i] = Char;
239 }
240 else
241 {
242 ConsoleInputUnicodeCharToAnsiChar(Console, &((PCHAR)Buffer)[i], &Char);
243 }
244 ++i;
245 }
246
247 if (Console->LinePos == Console->LineSize)
248 {
249 /* Entire line has been read */
250 ConsoleFreeHeap(Console->LineBuffer);
251 Console->LineBuffer = NULL;
252 }
253
254 Status = STATUS_SUCCESS;
255 }
256 }
257 else
258 {
259 /* Character input */
260 while (i < NumCharsToRead && !IsListEmpty(&InputBuffer->InputEvents))
261 {
262 /* Remove input event from queue */
263 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
264 if (IsListEmpty(&InputBuffer->InputEvents))
265 {
266 ResetEvent(InputBuffer->ActiveEvent);
267 }
268 Input = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
269
270 /* Only pay attention to valid ASCII chars, on key down */
271 if (Input->InputEvent.EventType == KEY_EVENT &&
272 Input->InputEvent.Event.KeyEvent.bKeyDown &&
273 Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar != L'\0')
274 {
275 WCHAR Char = Input->InputEvent.Event.KeyEvent.uChar.UnicodeChar;
276
277 if (Unicode)
278 {
279 ((PWCHAR)Buffer)[i] = Char;
280 }
281 else
282 {
283 ConsoleInputUnicodeCharToAnsiChar(Console, &((PCHAR)Buffer)[i], &Char);
284 }
285 ++i;
286
287 /* Did read something */
288 Status = STATUS_SUCCESS;
289 }
290 ConsoleFreeHeap(Input);
291 }
292 }
293
294 if (NumCharsRead) *NumCharsRead = i;
295
296 return Status;
297 }
298
299 NTSTATUS NTAPI
300 ConDrvGetConsoleInput(IN PCONSOLE Console,
301 IN PCONSOLE_INPUT_BUFFER InputBuffer,
302 IN BOOLEAN KeepEvents,
303 IN BOOLEAN WaitForMoreEvents,
304 IN BOOLEAN Unicode,
305 OUT PINPUT_RECORD InputRecord,
306 IN ULONG NumEventsToRead,
307 OUT PULONG NumEventsRead OPTIONAL)
308 {
309 PLIST_ENTRY CurrentInput;
310 ConsoleInput* Input;
311 ULONG i = 0;
312
313 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */)
314 return STATUS_INVALID_PARAMETER;
315
316 /* Validity checks */
317 ASSERT(Console == InputBuffer->Header.Console);
318 ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToRead == 0));
319
320 if (NumEventsRead) *NumEventsRead = 0;
321
322 if (IsListEmpty(&InputBuffer->InputEvents))
323 {
324 /*
325 * No input is available. Wait for more input if requested,
326 * otherwise, we don't wait, so we return success.
327 */
328 return (WaitForMoreEvents ? STATUS_PENDING : STATUS_SUCCESS);
329 }
330
331 /* Only get input if there is any */
332 CurrentInput = InputBuffer->InputEvents.Flink;
333 i = 0;
334 while ((CurrentInput != &InputBuffer->InputEvents) && (i < NumEventsToRead))
335 {
336 Input = CONTAINING_RECORD(CurrentInput, ConsoleInput, ListEntry);
337
338 *InputRecord = Input->InputEvent;
339
340 if (!Unicode)
341 {
342 ConioInputEventToAnsi(InputBuffer->Header.Console, InputRecord);
343 }
344
345 ++InputRecord;
346 ++i;
347 CurrentInput = CurrentInput->Flink;
348
349 /* Remove the events from the queue if needed */
350 if (!KeepEvents)
351 {
352 RemoveEntryList(&Input->ListEntry);
353 ConsoleFreeHeap(Input);
354 }
355 }
356
357 if (NumEventsRead) *NumEventsRead = i;
358
359 if (IsListEmpty(&InputBuffer->InputEvents))
360 {
361 ResetEvent(InputBuffer->ActiveEvent);
362 }
363
364 /* We read all the inputs available, we return success */
365 return STATUS_SUCCESS;
366 }
367
368 NTSTATUS NTAPI
369 ConDrvWriteConsoleInput(IN PCONSOLE Console,
370 IN PCONSOLE_INPUT_BUFFER InputBuffer,
371 IN BOOLEAN Unicode,
372 IN BOOLEAN AppendToEnd,
373 IN PINPUT_RECORD InputRecord,
374 IN ULONG NumEventsToWrite,
375 OUT PULONG NumEventsWritten OPTIONAL)
376 {
377 NTSTATUS Status = STATUS_SUCCESS;
378 ULONG i;
379
380 if (Console == NULL || InputBuffer == NULL /* || InputRecord == NULL */)
381 return STATUS_INVALID_PARAMETER;
382
383 /* Validity checks */
384 ASSERT(Console == InputBuffer->Header.Console);
385 ASSERT((InputRecord != NULL) || (InputRecord == NULL && NumEventsToWrite == 0));
386
387 // if (NumEventsWritten) *NumEventsWritten = 0;
388 // Status = ConioAddInputEvents(Console, InputRecord, NumEventsToWrite, NumEventsWritten, AppendToEnd);
389
390 for (i = 0; i < NumEventsToWrite && NT_SUCCESS(Status); ++i)
391 {
392 if (!Unicode)
393 {
394 ConioInputEventToUnicode(Console, InputRecord);
395 }
396
397 Status = ConioAddInputEvent(Console, InputRecord++, AppendToEnd);
398 }
399
400 if (NumEventsWritten) *NumEventsWritten = i;
401
402 return Status;
403 }
404
405 NTSTATUS NTAPI
406 ConDrvFlushConsoleInputBuffer(IN PCONSOLE Console,
407 IN PCONSOLE_INPUT_BUFFER InputBuffer)
408 {
409 PLIST_ENTRY CurrentEntry;
410 ConsoleInput* Event;
411
412 if (Console == NULL || InputBuffer == NULL)
413 return STATUS_INVALID_PARAMETER;
414
415 /* Validity check */
416 ASSERT(Console == InputBuffer->Header.Console);
417
418 /* Discard all entries in the input event queue */
419 while (!IsListEmpty(&InputBuffer->InputEvents))
420 {
421 CurrentEntry = RemoveHeadList(&InputBuffer->InputEvents);
422 Event = CONTAINING_RECORD(CurrentEntry, ConsoleInput, ListEntry);
423 ConsoleFreeHeap(Event);
424 }
425 ResetEvent(InputBuffer->ActiveEvent);
426
427 return STATUS_SUCCESS;
428 }
429
430 NTSTATUS NTAPI
431 ConDrvGetConsoleNumberOfInputEvents(IN PCONSOLE Console,
432 IN PCONSOLE_INPUT_BUFFER InputBuffer,
433 OUT PULONG NumberOfEvents)
434 {
435 PLIST_ENTRY CurrentInput;
436
437 if (Console == NULL || InputBuffer == NULL || NumberOfEvents == NULL)
438 return STATUS_INVALID_PARAMETER;
439
440 /* Validity check */
441 ASSERT(Console == InputBuffer->Header.Console);
442
443 *NumberOfEvents = 0;
444
445 /* If there are any events ... */
446 CurrentInput = InputBuffer->InputEvents.Flink;
447 while (CurrentInput != &InputBuffer->InputEvents)
448 {
449 CurrentInput = CurrentInput->Flink;
450 (*NumberOfEvents)++;
451 }
452
453 return STATUS_SUCCESS;
454 }
455
456 /* EOF */