a0fbe2dfe7006439193a9217ae8483881ad43e45
[reactos.git] / win32ss / user / consrv / lineinput.c
1 /*
2 * LICENSE: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Console Server DLL
4 * FILE: win32ss/user/consrv/tuiconsole.c
5 * PURPOSE: Console line input functions
6 * PROGRAMMERS: Jeffrey Morlan
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "consrv.h"
12 #include "conio.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 typedef struct tagHISTORY_BUFFER
18 {
19 LIST_ENTRY ListEntry;
20 WORD Position;
21 WORD MaxEntries;
22 WORD NumEntries;
23 PUNICODE_STRING Entries;
24 UNICODE_STRING ExeName;
25 } HISTORY_BUFFER, *PHISTORY_BUFFER;
26
27 /* FUNCTIONS *****************************************************************/
28
29 static PHISTORY_BUFFER
30 HistoryCurrentBuffer(PCSRSS_CONSOLE Console)
31 {
32 /* TODO: use actual EXE name sent from process that called ReadConsole */
33 UNICODE_STRING ExeName = { 14, 14, L"cmd.exe" };
34 PLIST_ENTRY Entry = Console->HistoryBuffers.Flink;
35 PHISTORY_BUFFER Hist;
36
37 for (; Entry != &Console->HistoryBuffers; Entry = Entry->Flink)
38 {
39 Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry);
40 if (RtlEqualUnicodeString(&ExeName, &Hist->ExeName, FALSE))
41 return Hist;
42 }
43
44 /* Couldn't find the buffer, create a new one */
45 Hist = HeapAlloc(ConSrvHeap, 0, sizeof(HISTORY_BUFFER) + ExeName.Length);
46 if (!Hist)
47 return NULL;
48 Hist->MaxEntries = Console->HistoryBufferSize;
49 Hist->NumEntries = 0;
50 Hist->Entries = HeapAlloc(ConSrvHeap, 0, Hist->MaxEntries * sizeof(UNICODE_STRING));
51 if (!Hist->Entries)
52 {
53 HeapFree(ConSrvHeap, 0, Hist);
54 return NULL;
55 }
56 Hist->ExeName.Length = Hist->ExeName.MaximumLength = ExeName.Length;
57 Hist->ExeName.Buffer = (PWCHAR)(Hist + 1);
58 memcpy(Hist->ExeName.Buffer, ExeName.Buffer, ExeName.Length);
59 InsertHeadList(&Console->HistoryBuffers, &Hist->ListEntry);
60 return Hist;
61 }
62
63 static VOID
64 HistoryAddEntry(PCSRSS_CONSOLE Console)
65 {
66 UNICODE_STRING NewEntry;
67 PHISTORY_BUFFER Hist;
68 INT i;
69
70 NewEntry.Length = NewEntry.MaximumLength = Console->LineSize * sizeof(WCHAR);
71 NewEntry.Buffer = Console->LineBuffer;
72
73 if (!(Hist = HistoryCurrentBuffer(Console)))
74 return;
75
76 /* Don't add blank or duplicate entries */
77 if (NewEntry.Length == 0 || Hist->MaxEntries == 0 ||
78 (Hist->NumEntries > 0 &&
79 RtlEqualUnicodeString(&Hist->Entries[Hist->NumEntries - 1], &NewEntry, FALSE)))
80 {
81 return;
82 }
83
84 if (Console->HistoryNoDup)
85 {
86 /* Check if this line has been entered before */
87 for (i = Hist->NumEntries - 1; i >= 0; i--)
88 {
89 if (RtlEqualUnicodeString(&Hist->Entries[i], &NewEntry, FALSE))
90 {
91 /* Just rotate the list to bring this entry to the end */
92 NewEntry = Hist->Entries[i];
93 memmove(&Hist->Entries[i], &Hist->Entries[i + 1],
94 (Hist->NumEntries - (i + 1)) * sizeof(UNICODE_STRING));
95 Hist->Entries[Hist->NumEntries - 1] = NewEntry;
96 Hist->Position = Hist->NumEntries - 1;
97 return;
98 }
99 }
100 }
101
102 if (Hist->NumEntries == Hist->MaxEntries)
103 {
104 /* List is full, remove oldest entry */
105 RtlFreeUnicodeString(&Hist->Entries[0]);
106 memmove(&Hist->Entries[0], &Hist->Entries[1],
107 --Hist->NumEntries * sizeof(UNICODE_STRING));
108 }
109
110 if (NT_SUCCESS(RtlDuplicateUnicodeString(0, &NewEntry, &Hist->Entries[Hist->NumEntries])))
111 Hist->NumEntries++;
112 Hist->Position = Hist->NumEntries - 1;
113 }
114
115 static VOID
116 HistoryGetCurrentEntry(PCSRSS_CONSOLE Console, PUNICODE_STRING Entry)
117 {
118 PHISTORY_BUFFER Hist;
119 if (!(Hist = HistoryCurrentBuffer(Console)) || Hist->NumEntries == 0)
120 Entry->Length = 0;
121 else
122 *Entry = Hist->Entries[Hist->Position];
123 }
124
125 static PHISTORY_BUFFER
126 HistoryFindBuffer(PCSRSS_CONSOLE Console, PUNICODE_STRING ExeName)
127 {
128 PLIST_ENTRY Entry = Console->HistoryBuffers.Flink;
129 while (Entry != &Console->HistoryBuffers)
130 {
131 /* For the history APIs, the caller is allowed to give only part of the name */
132 PHISTORY_BUFFER Hist = CONTAINING_RECORD(Entry, HISTORY_BUFFER, ListEntry);
133 if (RtlPrefixUnicodeString(ExeName, &Hist->ExeName, TRUE))
134 return Hist;
135 Entry = Entry->Flink;
136 }
137 return NULL;
138 }
139
140 VOID FASTCALL
141 HistoryDeleteBuffer(PHISTORY_BUFFER Hist)
142 {
143 if (!Hist)
144 return;
145 while (Hist->NumEntries != 0)
146 RtlFreeUnicodeString(&Hist->Entries[--Hist->NumEntries]);
147 HeapFree(ConSrvHeap, 0, Hist->Entries);
148 RemoveEntryList(&Hist->ListEntry);
149 HeapFree(ConSrvHeap, 0, Hist);
150 }
151
152 CSR_API(SrvGetConsoleCommandHistoryLength)
153 {
154 PCSRSS_GET_COMMAND_HISTORY_LENGTH GetCommandHistoryLength = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetCommandHistoryLength;
155 PCSR_PROCESS Process = CsrGetClientThread()->Process;
156 PCSRSS_CONSOLE Console;
157 NTSTATUS Status;
158 PHISTORY_BUFFER Hist;
159 ULONG Length = 0;
160 INT i;
161
162 if (!Win32CsrValidateBuffer(Process,
163 GetCommandHistoryLength->ExeName.Buffer,
164 GetCommandHistoryLength->ExeName.Length, 1))
165 {
166 return STATUS_ACCESS_VIOLATION;
167 }
168
169 Status = ConioConsoleFromProcessData(ConsoleGetPerProcessData(Process), &Console);
170 if (NT_SUCCESS(Status))
171 {
172 Hist = HistoryFindBuffer(Console, &GetCommandHistoryLength->ExeName);
173 if (Hist)
174 {
175 for (i = 0; i < Hist->NumEntries; i++)
176 Length += Hist->Entries[i].Length + sizeof(WCHAR);
177 }
178 GetCommandHistoryLength->Length = Length;
179 ConioUnlockConsole(Console);
180 }
181 return Status;
182 }
183
184 CSR_API(SrvGetConsoleCommandHistory)
185 {
186 PCSRSS_GET_COMMAND_HISTORY GetCommandHistory = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetCommandHistory;
187 PCSR_PROCESS Process = CsrGetClientThread()->Process;
188 PCSRSS_CONSOLE Console;
189 NTSTATUS Status;
190 PHISTORY_BUFFER Hist;
191 PBYTE Buffer = (PBYTE)GetCommandHistory->History;
192 ULONG BufferSize = GetCommandHistory->Length;
193 INT i;
194
195 if (!Win32CsrValidateBuffer(Process, Buffer, BufferSize, 1) ||
196 !Win32CsrValidateBuffer(Process,
197 GetCommandHistory->ExeName.Buffer,
198 GetCommandHistory->ExeName.Length, 1))
199 {
200 return STATUS_ACCESS_VIOLATION;
201 }
202
203 Status = ConioConsoleFromProcessData(ConsoleGetPerProcessData(Process), &Console);
204 if (NT_SUCCESS(Status))
205 {
206 Hist = HistoryFindBuffer(Console, &GetCommandHistory->ExeName);
207 if (Hist)
208 {
209 for (i = 0; i < Hist->NumEntries; i++)
210 {
211 if (BufferSize < (Hist->Entries[i].Length + sizeof(WCHAR)))
212 {
213 Status = STATUS_BUFFER_OVERFLOW;
214 break;
215 }
216 memcpy(Buffer, Hist->Entries[i].Buffer, Hist->Entries[i].Length);
217 Buffer += Hist->Entries[i].Length;
218 *(PWCHAR)Buffer = L'\0';
219 Buffer += sizeof(WCHAR);
220 }
221 }
222 GetCommandHistory->Length = Buffer - (PBYTE)GetCommandHistory->History;
223 ConioUnlockConsole(Console);
224 }
225 return Status;
226 }
227
228 CSR_API(SrvExpungeConsoleCommandHistory)
229 {
230 PCSRSS_EXPUNGE_COMMAND_HISTORY ExpungeCommandHistory = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.ExpungeCommandHistory;
231 PCSR_PROCESS Process = CsrGetClientThread()->Process;
232 PCSRSS_CONSOLE Console;
233 PHISTORY_BUFFER Hist;
234 NTSTATUS Status;
235
236 if (!Win32CsrValidateBuffer(Process,
237 ExpungeCommandHistory->ExeName.Buffer,
238 ExpungeCommandHistory->ExeName.Length, 1))
239 {
240 return STATUS_ACCESS_VIOLATION;
241 }
242
243 Status = ConioConsoleFromProcessData(ConsoleGetPerProcessData(Process), &Console);
244 if (NT_SUCCESS(Status))
245 {
246 Hist = HistoryFindBuffer(Console, &ExpungeCommandHistory->ExeName);
247 HistoryDeleteBuffer(Hist);
248 ConioUnlockConsole(Console);
249 }
250 return Status;
251 }
252
253 CSR_API(SrvSetConsoleNumberOfCommands)
254 {
255 PCSRSS_SET_HISTORY_NUMBER_COMMANDS SetHistoryNumberCommands = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetHistoryNumberCommands;
256 PCSR_PROCESS Process = CsrGetClientThread()->Process;
257 PCSRSS_CONSOLE Console;
258 PHISTORY_BUFFER Hist;
259 NTSTATUS Status;
260 WORD MaxEntries = SetHistoryNumberCommands->NumCommands;
261 PUNICODE_STRING OldEntryList, NewEntryList;
262
263 if (!Win32CsrValidateBuffer(Process,
264 SetHistoryNumberCommands->ExeName.Buffer,
265 SetHistoryNumberCommands->ExeName.Length, 1))
266 {
267 return STATUS_ACCESS_VIOLATION;
268 }
269
270 Status = ConioConsoleFromProcessData(ConsoleGetPerProcessData(Process), &Console);
271 if (NT_SUCCESS(Status))
272 {
273 Hist = HistoryFindBuffer(Console, &SetHistoryNumberCommands->ExeName);
274 if (Hist)
275 {
276 OldEntryList = Hist->Entries;
277 NewEntryList = HeapAlloc(ConSrvHeap, 0,
278 MaxEntries * sizeof(UNICODE_STRING));
279 if (!NewEntryList)
280 {
281 Status = STATUS_NO_MEMORY;
282 }
283 else
284 {
285 /* If necessary, shrink by removing oldest entries */
286 for (; Hist->NumEntries > MaxEntries; Hist->NumEntries--)
287 {
288 RtlFreeUnicodeString(Hist->Entries++);
289 Hist->Position += (Hist->Position == 0);
290 }
291
292 Hist->MaxEntries = MaxEntries;
293 Hist->Entries = memcpy(NewEntryList, Hist->Entries,
294 Hist->NumEntries * sizeof(UNICODE_STRING));
295 HeapFree(ConSrvHeap, 0, OldEntryList);
296 }
297 }
298 ConioUnlockConsole(Console);
299 }
300 return Status;
301 }
302
303 CSR_API(SrvGetConsoleHistory)
304 {
305 PCSRSS_GET_HISTORY_INFO GetHistoryInfo = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.GetHistoryInfo;
306 PCSRSS_CONSOLE Console;
307 NTSTATUS Status = ConioConsoleFromProcessData(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console);
308 if (NT_SUCCESS(Status))
309 {
310 GetHistoryInfo->HistoryBufferSize = Console->HistoryBufferSize;
311 GetHistoryInfo->NumberOfHistoryBuffers = Console->NumberOfHistoryBuffers;
312 GetHistoryInfo->dwFlags = Console->HistoryNoDup;
313 ConioUnlockConsole(Console);
314 }
315 return Status;
316 }
317
318 CSR_API(SrvSetConsoleHistory)
319 {
320 PCSRSS_SET_HISTORY_INFO SetHistoryInfo = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.SetHistoryInfo;
321 PCSRSS_CONSOLE Console;
322 NTSTATUS Status = ConioConsoleFromProcessData(ConsoleGetPerProcessData(CsrGetClientThread()->Process), &Console);
323 if (NT_SUCCESS(Status))
324 {
325 Console->HistoryBufferSize = (WORD)SetHistoryInfo->HistoryBufferSize;
326 Console->NumberOfHistoryBuffers = (WORD)SetHistoryInfo->NumberOfHistoryBuffers;
327 Console->HistoryNoDup = SetHistoryInfo->dwFlags & HISTORY_NO_DUP_FLAG;
328 ConioUnlockConsole(Console);
329 }
330 return Status;
331 }
332
333 static VOID
334 LineInputSetPos(PCSRSS_CONSOLE Console, UINT Pos)
335 {
336 if (Pos != Console->LinePos && Console->Mode & ENABLE_ECHO_INPUT)
337 {
338 PCSRSS_SCREEN_BUFFER Buffer = Console->ActiveBuffer;
339 UINT OldCursorX = Buffer->CurrentX;
340 UINT OldCursorY = Buffer->CurrentY;
341 INT XY = OldCursorY * Buffer->MaxX + OldCursorX;
342
343 XY += (Pos - Console->LinePos);
344 if (XY < 0)
345 XY = 0;
346 else if (XY >= Buffer->MaxY * Buffer->MaxX)
347 XY = Buffer->MaxY * Buffer->MaxX - 1;
348
349 Buffer->CurrentX = XY % Buffer->MaxX;
350 Buffer->CurrentY = XY / Buffer->MaxX;
351 ConioSetScreenInfo(Console, Buffer, OldCursorX, OldCursorY);
352 }
353
354 Console->LinePos = Pos;
355 }
356
357 static VOID
358 LineInputEdit(PCSRSS_CONSOLE Console, UINT NumToDelete, UINT NumToInsert, WCHAR *Insertion)
359 {
360 UINT Pos = Console->LinePos;
361 UINT NewSize = Console->LineSize - NumToDelete + NumToInsert;
362 INT i;
363
364 /* Make sure there's always enough room for ending \r\n */
365 if (NewSize + 2 > Console->LineMaxSize)
366 return;
367
368 memmove(&Console->LineBuffer[Pos + NumToInsert],
369 &Console->LineBuffer[Pos + NumToDelete],
370 (Console->LineSize - (Pos + NumToDelete)) * sizeof(WCHAR));
371 memcpy(&Console->LineBuffer[Pos], Insertion, NumToInsert * sizeof(WCHAR));
372
373 if (Console->Mode & ENABLE_ECHO_INPUT)
374 {
375 for (i = Pos; i < NewSize; i++)
376 {
377 CHAR AsciiChar;
378 WideCharToMultiByte(Console->OutputCodePage, 0,
379 &Console->LineBuffer[i], 1,
380 &AsciiChar, 1, NULL, NULL);
381 ConioWriteConsole(Console, Console->ActiveBuffer, &AsciiChar, 1, TRUE);
382 }
383 for (; i < Console->LineSize; i++)
384 {
385 ConioWriteConsole(Console, Console->ActiveBuffer, " ", 1, TRUE);
386 }
387 Console->LinePos = i;
388 }
389
390 Console->LineSize = NewSize;
391 LineInputSetPos(Console, Pos + NumToInsert);
392 }
393
394 static VOID
395 LineInputRecallHistory(PCSRSS_CONSOLE Console, INT Offset)
396 {
397 PHISTORY_BUFFER Hist;
398
399 if (!(Hist = HistoryCurrentBuffer(Console)) || Hist->NumEntries == 0)
400 return;
401
402 Offset += Hist->Position;
403 Offset = max(Offset, 0);
404 Offset = min(Offset, Hist->NumEntries - 1);
405 Hist->Position = Offset;
406
407 LineInputSetPos(Console, 0);
408 LineInputEdit(Console, Console->LineSize,
409 Hist->Entries[Offset].Length / sizeof(WCHAR),
410 Hist->Entries[Offset].Buffer);
411 }
412
413 VOID FASTCALL
414 LineInputKeyDown(PCSRSS_CONSOLE Console, KEY_EVENT_RECORD *KeyEvent)
415 {
416 UINT Pos = Console->LinePos;
417 PHISTORY_BUFFER Hist;
418 UNICODE_STRING Entry;
419 INT HistPos;
420
421 switch (KeyEvent->wVirtualKeyCode)
422 {
423 case VK_ESCAPE:
424 /* Clear entire line */
425 LineInputSetPos(Console, 0);
426 LineInputEdit(Console, Console->LineSize, 0, NULL);
427 return;
428 case VK_HOME:
429 /* Move to start of line. With ctrl, erase everything left of cursor */
430 LineInputSetPos(Console, 0);
431 if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
432 LineInputEdit(Console, Pos, 0, NULL);
433 return;
434 case VK_END:
435 /* Move to end of line. With ctrl, erase everything right of cursor */
436 if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
437 LineInputEdit(Console, Console->LineSize - Pos, 0, NULL);
438 else
439 LineInputSetPos(Console, Console->LineSize);
440 return;
441 case VK_LEFT:
442 /* Move left. With ctrl, move to beginning of previous word */
443 if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
444 {
445 while (Pos > 0 && Console->LineBuffer[Pos - 1] == L' ') Pos--;
446 while (Pos > 0 && Console->LineBuffer[Pos - 1] != L' ') Pos--;
447 }
448 else
449 {
450 Pos -= (Pos > 0);
451 }
452 LineInputSetPos(Console, Pos);
453 return;
454 case VK_RIGHT:
455 case VK_F1:
456 /* Move right. With ctrl, move to beginning of next word */
457 if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
458 {
459 while (Pos < Console->LineSize && Console->LineBuffer[Pos] != L' ') Pos++;
460 while (Pos < Console->LineSize && Console->LineBuffer[Pos] == L' ') Pos++;
461 LineInputSetPos(Console, Pos);
462 return;
463 }
464 else
465 {
466 /* Recall one character (but don't overwrite current line) */
467 HistoryGetCurrentEntry(Console, &Entry);
468 if (Pos < Console->LineSize)
469 LineInputSetPos(Console, Pos + 1);
470 else if (Pos * sizeof(WCHAR) < Entry.Length)
471 LineInputEdit(Console, 0, 1, &Entry.Buffer[Pos]);
472 }
473 return;
474 case VK_INSERT:
475 /* Toggle between insert and overstrike */
476 Console->LineInsertToggle = !Console->LineInsertToggle;
477 ConioSetCursorInfo(Console, Console->ActiveBuffer);
478 return;
479 case VK_DELETE:
480 /* Remove character to right of cursor */
481 if (Pos != Console->LineSize)
482 LineInputEdit(Console, 1, 0, NULL);
483 return;
484 case VK_PRIOR:
485 /* Recall first history entry */
486 LineInputRecallHistory(Console, -((WORD)-1));
487 return;
488 case VK_NEXT:
489 /* Recall last history entry */
490 LineInputRecallHistory(Console, +((WORD)-1));
491 return;
492 case VK_UP:
493 case VK_F5:
494 /* Recall previous history entry. On first time, actually recall the
495 * current (usually last) entry; on subsequent times go back. */
496 LineInputRecallHistory(Console, Console->LineUpPressed ? -1 : 0);
497 Console->LineUpPressed = TRUE;
498 return;
499 case VK_DOWN:
500 /* Recall next history entry */
501 LineInputRecallHistory(Console, +1);
502 return;
503 case VK_F3:
504 /* Recall remainder of current history entry */
505 HistoryGetCurrentEntry(Console, &Entry);
506 if (Pos * sizeof(WCHAR) < Entry.Length)
507 {
508 UINT InsertSize = (Entry.Length / sizeof(WCHAR) - Pos);
509 UINT DeleteSize = min(Console->LineSize - Pos, InsertSize);
510 LineInputEdit(Console, DeleteSize, InsertSize, &Entry.Buffer[Pos]);
511 }
512 return;
513 case VK_F6:
514 /* Insert a ^Z character */
515 KeyEvent->uChar.UnicodeChar = 26;
516 break;
517 case VK_F7:
518 if (KeyEvent->dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
519 HistoryDeleteBuffer(HistoryCurrentBuffer(Console));
520 return;
521 case VK_F8:
522 /* Search for history entries starting with input. */
523 if (!(Hist = HistoryCurrentBuffer(Console)) || Hist->NumEntries == 0)
524 return;
525
526 /* Like Up/F5, on first time start from current (usually last) entry,
527 * but on subsequent times start at previous entry. */
528 if (Console->LineUpPressed)
529 Hist->Position = (Hist->Position ? Hist->Position : Hist->NumEntries) - 1;
530 Console->LineUpPressed = TRUE;
531
532 Entry.Length = Console->LinePos * sizeof(WCHAR);
533 Entry.Buffer = Console->LineBuffer;
534
535 /* Keep going backwards, even wrapping around to the end,
536 * until we get back to starting point */
537 HistPos = Hist->Position;
538 do
539 {
540 if (RtlPrefixUnicodeString(&Entry, &Hist->Entries[HistPos], FALSE))
541 {
542 Hist->Position = HistPos;
543 LineInputEdit(Console, Console->LineSize - Pos,
544 Hist->Entries[HistPos].Length / sizeof(WCHAR) - Pos,
545 &Hist->Entries[HistPos].Buffer[Pos]);
546 /* Cursor stays where it was */
547 LineInputSetPos(Console, Pos);
548 return;
549 }
550 if (--HistPos < 0) HistPos += Hist->NumEntries;
551 } while (HistPos != Hist->Position);
552 return;
553 }
554
555 if (KeyEvent->uChar.UnicodeChar == L'\b' && Console->Mode & ENABLE_PROCESSED_INPUT)
556 {
557 /* backspace handling - if processed input enabled then we handle it here
558 * otherwise we treat it like a normal char. */
559 if (Pos > 0)
560 {
561 LineInputSetPos(Console, Pos - 1);
562 LineInputEdit(Console, 1, 0, NULL);
563 }
564 }
565 else if (KeyEvent->uChar.UnicodeChar == L'\r')
566 {
567 HistoryAddEntry(Console);
568
569 /* TODO: Expand aliases */
570
571 LineInputSetPos(Console, Console->LineSize);
572 Console->LineBuffer[Console->LineSize++] = L'\r';
573 if (Console->Mode & ENABLE_ECHO_INPUT)
574 ConioWriteConsole(Console, Console->ActiveBuffer, "\r", 1, TRUE);
575
576 /* Add \n if processed input. There should usually be room for it,
577 * but an exception to the rule exists: the buffer could have been
578 * pre-filled with LineMaxSize - 1 characters. */
579 if (Console->Mode & ENABLE_PROCESSED_INPUT &&
580 Console->LineSize < Console->LineMaxSize)
581 {
582 Console->LineBuffer[Console->LineSize++] = L'\n';
583 if (Console->Mode & ENABLE_ECHO_INPUT)
584 ConioWriteConsole(Console, Console->ActiveBuffer, "\n", 1, TRUE);
585 }
586 Console->LineComplete = TRUE;
587 Console->LinePos = 0;
588 }
589 else if (KeyEvent->uChar.UnicodeChar != L'\0')
590 {
591 if (KeyEvent->uChar.UnicodeChar < 0x20 &&
592 Console->LineWakeupMask & (1 << KeyEvent->uChar.UnicodeChar))
593 {
594 /* Control key client wants to handle itself (e.g. for tab completion) */
595 Console->LineBuffer[Console->LineSize++] = L' ';
596 Console->LineBuffer[Console->LinePos] = KeyEvent->uChar.UnicodeChar;
597 Console->LineComplete = TRUE;
598 Console->LinePos = 0;
599 }
600 else
601 {
602 /* Normal character */
603 BOOL Overstrike = Console->LineInsertToggle && Console->LinePos != Console->LineSize;
604 LineInputEdit(Console, Overstrike, 1, &KeyEvent->uChar.UnicodeChar);
605 }
606 }
607 }
608
609 /* EOF */