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