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
9 /* INCLUDES ******************************************************************/
17 typedef struct tagHISTORY_BUFFER
23 PUNICODE_STRING Entries
;
24 UNICODE_STRING ExeName
;
25 } HISTORY_BUFFER
, *PHISTORY_BUFFER
;
27 /* FUNCTIONS *****************************************************************/
29 static PHISTORY_BUFFER
30 HistoryCurrentBuffer(PCSRSS_CONSOLE Console
)
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
;
37 for (; Entry
!= &Console
->HistoryBuffers
; Entry
= Entry
->Flink
)
39 Hist
= CONTAINING_RECORD(Entry
, HISTORY_BUFFER
, ListEntry
);
40 if (RtlEqualUnicodeString(&ExeName
, &Hist
->ExeName
, FALSE
))
44 /* Couldn't find the buffer, create a new one */
45 Hist
= HeapAlloc(ConSrvHeap
, 0, sizeof(HISTORY_BUFFER
) + ExeName
.Length
);
48 Hist
->MaxEntries
= Console
->HistoryBufferSize
;
50 Hist
->Entries
= HeapAlloc(ConSrvHeap
, 0, Hist
->MaxEntries
* sizeof(UNICODE_STRING
));
53 HeapFree(ConSrvHeap
, 0, Hist
);
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
);
64 HistoryAddEntry(PCSRSS_CONSOLE Console
)
66 UNICODE_STRING NewEntry
;
70 NewEntry
.Length
= NewEntry
.MaximumLength
= Console
->LineSize
* sizeof(WCHAR
);
71 NewEntry
.Buffer
= Console
->LineBuffer
;
73 if (!(Hist
= HistoryCurrentBuffer(Console
)))
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
)))
84 if (Console
->HistoryNoDup
)
86 /* Check if this line has been entered before */
87 for (i
= Hist
->NumEntries
- 1; i
>= 0; i
--)
89 if (RtlEqualUnicodeString(&Hist
->Entries
[i
], &NewEntry
, FALSE
))
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;
102 if (Hist
->NumEntries
== Hist
->MaxEntries
)
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
));
110 if (NT_SUCCESS(RtlDuplicateUnicodeString(0, &NewEntry
, &Hist
->Entries
[Hist
->NumEntries
])))
112 Hist
->Position
= Hist
->NumEntries
- 1;
116 HistoryGetCurrentEntry(PCSRSS_CONSOLE Console
, PUNICODE_STRING Entry
)
118 PHISTORY_BUFFER Hist
;
119 if (!(Hist
= HistoryCurrentBuffer(Console
)) || Hist
->NumEntries
== 0)
122 *Entry
= Hist
->Entries
[Hist
->Position
];
125 static PHISTORY_BUFFER
126 HistoryFindBuffer(PCSRSS_CONSOLE Console
, PUNICODE_STRING ExeName
)
128 PLIST_ENTRY Entry
= Console
->HistoryBuffers
.Flink
;
129 while (Entry
!= &Console
->HistoryBuffers
)
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
))
135 Entry
= Entry
->Flink
;
141 HistoryDeleteBuffer(PHISTORY_BUFFER Hist
)
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
);
152 CSR_API(SrvGetConsoleCommandHistoryLength
)
154 PCONSOLE_GETCOMMANDHISTORYLENGTH GetCommandHistoryLengthRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetCommandHistoryLengthRequest
;
155 PCONSOLE_PROCESS_DATA ProcessData
= ConsoleGetPerProcessData(CsrGetClientThread()->Process
);
156 PCSRSS_CONSOLE Console
;
158 PHISTORY_BUFFER Hist
;
162 if (!CsrValidateMessageBuffer(ApiMessage
,
163 (PVOID
*)&GetCommandHistoryLengthRequest
->ExeName
.Buffer
,
164 GetCommandHistoryLengthRequest
->ExeName
.Length
,
167 return STATUS_INVALID_PARAMETER
;
170 Status
= ConioConsoleFromProcessData(ProcessData
, &Console
);
171 if (NT_SUCCESS(Status
))
173 Hist
= HistoryFindBuffer(Console
, &GetCommandHistoryLengthRequest
->ExeName
);
176 for (i
= 0; i
< Hist
->NumEntries
; i
++)
177 Length
+= Hist
->Entries
[i
].Length
+ sizeof(WCHAR
);
179 GetCommandHistoryLengthRequest
->Length
= Length
;
180 ConioUnlockConsole(Console
);
185 CSR_API(SrvGetConsoleCommandHistory
)
187 PCONSOLE_GETCOMMANDHISTORY GetCommandHistoryRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.GetCommandHistoryRequest
;
188 PCONSOLE_PROCESS_DATA ProcessData
= ConsoleGetPerProcessData(CsrGetClientThread()->Process
);
189 PCSRSS_CONSOLE Console
;
191 PHISTORY_BUFFER Hist
;
192 PBYTE Buffer
= (PBYTE
)GetCommandHistoryRequest
->History
;
193 ULONG BufferSize
= GetCommandHistoryRequest
->Length
;
196 if ( !CsrValidateMessageBuffer(ApiMessage
,
197 (PVOID
*)&GetCommandHistoryRequest
->History
,
198 GetCommandHistoryRequest
->Length
,
200 !CsrValidateMessageBuffer(ApiMessage
,
201 (PVOID
*)&GetCommandHistoryRequest
->ExeName
.Buffer
,
202 GetCommandHistoryRequest
->ExeName
.Length
,
205 return STATUS_INVALID_PARAMETER
;
208 Status
= ConioConsoleFromProcessData(ProcessData
, &Console
);
209 if (NT_SUCCESS(Status
))
211 Hist
= HistoryFindBuffer(Console
, &GetCommandHistoryRequest
->ExeName
);
214 for (i
= 0; i
< Hist
->NumEntries
; i
++)
216 if (BufferSize
< (Hist
->Entries
[i
].Length
+ sizeof(WCHAR
)))
218 Status
= STATUS_BUFFER_OVERFLOW
;
221 memcpy(Buffer
, Hist
->Entries
[i
].Buffer
, Hist
->Entries
[i
].Length
);
222 Buffer
+= Hist
->Entries
[i
].Length
;
223 *(PWCHAR
)Buffer
= L
'\0';
224 Buffer
+= sizeof(WCHAR
);
227 GetCommandHistoryRequest
->Length
= Buffer
- (PBYTE
)GetCommandHistoryRequest
->History
;
228 ConioUnlockConsole(Console
);
233 CSR_API(SrvExpungeConsoleCommandHistory
)
235 PCONSOLE_EXPUNGECOMMANDHISTORY ExpungeCommandHistoryRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.ExpungeCommandHistoryRequest
;
236 PCONSOLE_PROCESS_DATA ProcessData
= ConsoleGetPerProcessData(CsrGetClientThread()->Process
);
237 PCSRSS_CONSOLE Console
;
238 PHISTORY_BUFFER Hist
;
241 if (!CsrValidateMessageBuffer(ApiMessage
,
242 (PVOID
*)&ExpungeCommandHistoryRequest
->ExeName
.Buffer
,
243 ExpungeCommandHistoryRequest
->ExeName
.Length
,
246 return STATUS_INVALID_PARAMETER
;
249 Status
= ConioConsoleFromProcessData(ProcessData
, &Console
);
250 if (NT_SUCCESS(Status
))
252 Hist
= HistoryFindBuffer(Console
, &ExpungeCommandHistoryRequest
->ExeName
);
253 HistoryDeleteBuffer(Hist
);
254 ConioUnlockConsole(Console
);
259 CSR_API(SrvSetConsoleNumberOfCommands
)
261 PCONSOLE_SETHISTORYNUMBERCOMMANDS SetHistoryNumberCommandsRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.SetHistoryNumberCommandsRequest
;
262 PCONSOLE_PROCESS_DATA ProcessData
= ConsoleGetPerProcessData(CsrGetClientThread()->Process
);
263 PCSRSS_CONSOLE Console
;
264 PHISTORY_BUFFER Hist
;
266 UINT MaxEntries
= SetHistoryNumberCommandsRequest
->NumCommands
;
267 PUNICODE_STRING OldEntryList
, NewEntryList
;
269 if (!CsrValidateMessageBuffer(ApiMessage
,
270 (PVOID
*)&SetHistoryNumberCommandsRequest
->ExeName
.Buffer
,
271 SetHistoryNumberCommandsRequest
->ExeName
.Length
,
274 return STATUS_INVALID_PARAMETER
;
277 Status
= ConioConsoleFromProcessData(ProcessData
, &Console
);
278 if (NT_SUCCESS(Status
))
280 Hist
= HistoryFindBuffer(Console
, &SetHistoryNumberCommandsRequest
->ExeName
);
283 OldEntryList
= Hist
->Entries
;
284 NewEntryList
= HeapAlloc(ConSrvHeap
, 0,
285 MaxEntries
* sizeof(UNICODE_STRING
));
288 Status
= STATUS_NO_MEMORY
;
292 /* If necessary, shrink by removing oldest entries */
293 for (; Hist
->NumEntries
> MaxEntries
; Hist
->NumEntries
--)
295 RtlFreeUnicodeString(Hist
->Entries
++);
296 Hist
->Position
+= (Hist
->Position
== 0);
299 Hist
->MaxEntries
= MaxEntries
;
300 Hist
->Entries
= memcpy(NewEntryList
, Hist
->Entries
,
301 Hist
->NumEntries
* sizeof(UNICODE_STRING
));
302 HeapFree(ConSrvHeap
, 0, OldEntryList
);
305 ConioUnlockConsole(Console
);
310 CSR_API(SrvGetConsoleHistory
)
312 PCONSOLE_GETSETHISTORYINFO HistoryInfoRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.HistoryInfoRequest
;
313 PCSRSS_CONSOLE Console
;
314 NTSTATUS Status
= ConioConsoleFromProcessData(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
);
315 if (NT_SUCCESS(Status
))
317 HistoryInfoRequest
->HistoryBufferSize
= Console
->HistoryBufferSize
;
318 HistoryInfoRequest
->NumberOfHistoryBuffers
= Console
->NumberOfHistoryBuffers
;
319 HistoryInfoRequest
->dwFlags
= Console
->HistoryNoDup
;
320 ConioUnlockConsole(Console
);
325 CSR_API(SrvSetConsoleHistory
)
327 PCONSOLE_GETSETHISTORYINFO HistoryInfoRequest
= &((PCONSOLE_API_MESSAGE
)ApiMessage
)->Data
.HistoryInfoRequest
;
328 PCSRSS_CONSOLE Console
;
329 NTSTATUS Status
= ConioConsoleFromProcessData(ConsoleGetPerProcessData(CsrGetClientThread()->Process
), &Console
);
330 if (NT_SUCCESS(Status
))
332 Console
->HistoryBufferSize
= HistoryInfoRequest
->HistoryBufferSize
;
333 Console
->NumberOfHistoryBuffers
= HistoryInfoRequest
->NumberOfHistoryBuffers
;
334 Console
->HistoryNoDup
= HistoryInfoRequest
->dwFlags
& HISTORY_NO_DUP_FLAG
;
335 ConioUnlockConsole(Console
);
341 LineInputSetPos(PCSRSS_CONSOLE Console
, UINT Pos
)
343 if (Pos
!= Console
->LinePos
&& Console
->Mode
& ENABLE_ECHO_INPUT
)
345 PCSRSS_SCREEN_BUFFER Buffer
= Console
->ActiveBuffer
;
346 UINT OldCursorX
= Buffer
->CurrentX
;
347 UINT OldCursorY
= Buffer
->CurrentY
;
348 INT XY
= OldCursorY
* Buffer
->MaxX
+ OldCursorX
;
350 XY
+= (Pos
- Console
->LinePos
);
353 else if (XY
>= Buffer
->MaxY
* Buffer
->MaxX
)
354 XY
= Buffer
->MaxY
* Buffer
->MaxX
- 1;
356 Buffer
->CurrentX
= XY
% Buffer
->MaxX
;
357 Buffer
->CurrentY
= XY
/ Buffer
->MaxX
;
358 ConioSetScreenInfo(Console
, Buffer
, OldCursorX
, OldCursorY
);
361 Console
->LinePos
= Pos
;
365 LineInputEdit(PCSRSS_CONSOLE Console
, UINT NumToDelete
, UINT NumToInsert
, WCHAR
*Insertion
)
367 UINT Pos
= Console
->LinePos
;
368 UINT NewSize
= Console
->LineSize
- NumToDelete
+ NumToInsert
;
371 /* Make sure there's always enough room for ending \r\n */
372 if (NewSize
+ 2 > Console
->LineMaxSize
)
375 memmove(&Console
->LineBuffer
[Pos
+ NumToInsert
],
376 &Console
->LineBuffer
[Pos
+ NumToDelete
],
377 (Console
->LineSize
- (Pos
+ NumToDelete
)) * sizeof(WCHAR
));
378 memcpy(&Console
->LineBuffer
[Pos
], Insertion
, NumToInsert
* sizeof(WCHAR
));
380 if (Console
->Mode
& ENABLE_ECHO_INPUT
)
382 for (i
= Pos
; i
< NewSize
; i
++)
385 WideCharToMultiByte(Console
->OutputCodePage
, 0,
386 &Console
->LineBuffer
[i
], 1,
387 &AsciiChar
, 1, NULL
, NULL
);
388 ConioWriteConsole(Console
, Console
->ActiveBuffer
, &AsciiChar
, 1, TRUE
);
390 for (; i
< Console
->LineSize
; i
++)
392 ConioWriteConsole(Console
, Console
->ActiveBuffer
, " ", 1, TRUE
);
394 Console
->LinePos
= i
;
397 Console
->LineSize
= NewSize
;
398 LineInputSetPos(Console
, Pos
+ NumToInsert
);
402 LineInputRecallHistory(PCSRSS_CONSOLE Console
, INT Offset
)
404 PHISTORY_BUFFER Hist
;
406 if (!(Hist
= HistoryCurrentBuffer(Console
)) || Hist
->NumEntries
== 0)
409 Offset
+= Hist
->Position
;
410 Offset
= max(Offset
, 0);
411 Offset
= min(Offset
, Hist
->NumEntries
- 1);
412 Hist
->Position
= Offset
;
414 LineInputSetPos(Console
, 0);
415 LineInputEdit(Console
, Console
->LineSize
,
416 Hist
->Entries
[Offset
].Length
/ sizeof(WCHAR
),
417 Hist
->Entries
[Offset
].Buffer
);
421 LineInputKeyDown(PCSRSS_CONSOLE Console
, KEY_EVENT_RECORD
*KeyEvent
)
423 UINT Pos
= Console
->LinePos
;
424 PHISTORY_BUFFER Hist
;
425 UNICODE_STRING Entry
;
428 switch (KeyEvent
->wVirtualKeyCode
)
431 /* Clear entire line */
432 LineInputSetPos(Console
, 0);
433 LineInputEdit(Console
, Console
->LineSize
, 0, NULL
);
436 /* Move to start of line. With ctrl, erase everything left of cursor */
437 LineInputSetPos(Console
, 0);
438 if (KeyEvent
->dwControlKeyState
& (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
))
439 LineInputEdit(Console
, Pos
, 0, NULL
);
442 /* Move to end of line. With ctrl, erase everything right of cursor */
443 if (KeyEvent
->dwControlKeyState
& (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
))
444 LineInputEdit(Console
, Console
->LineSize
- Pos
, 0, NULL
);
446 LineInputSetPos(Console
, Console
->LineSize
);
449 /* Move left. With ctrl, move to beginning of previous word */
450 if (KeyEvent
->dwControlKeyState
& (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
))
452 while (Pos
> 0 && Console
->LineBuffer
[Pos
- 1] == L
' ') Pos
--;
453 while (Pos
> 0 && Console
->LineBuffer
[Pos
- 1] != L
' ') Pos
--;
459 LineInputSetPos(Console
, Pos
);
463 /* Move right. With ctrl, move to beginning of next word */
464 if (KeyEvent
->dwControlKeyState
& (LEFT_CTRL_PRESSED
| RIGHT_CTRL_PRESSED
))
466 while (Pos
< Console
->LineSize
&& Console
->LineBuffer
[Pos
] != L
' ') Pos
++;
467 while (Pos
< Console
->LineSize
&& Console
->LineBuffer
[Pos
] == L
' ') Pos
++;
468 LineInputSetPos(Console
, Pos
);
473 /* Recall one character (but don't overwrite current line) */
474 HistoryGetCurrentEntry(Console
, &Entry
);
475 if (Pos
< Console
->LineSize
)
476 LineInputSetPos(Console
, Pos
+ 1);
477 else if (Pos
* sizeof(WCHAR
) < Entry
.Length
)
478 LineInputEdit(Console
, 0, 1, &Entry
.Buffer
[Pos
]);
482 /* Toggle between insert and overstrike */
483 Console
->LineInsertToggle
= !Console
->LineInsertToggle
;
484 ConioSetCursorInfo(Console
, Console
->ActiveBuffer
);
487 /* Remove character to right of cursor */
488 if (Pos
!= Console
->LineSize
)
489 LineInputEdit(Console
, 1, 0, NULL
);
492 /* Recall first history entry */
493 LineInputRecallHistory(Console
, -((WORD
)-1));
496 /* Recall last history entry */
497 LineInputRecallHistory(Console
, +((WORD
)-1));
501 /* Recall previous history entry. On first time, actually recall the
502 * current (usually last) entry; on subsequent times go back. */
503 LineInputRecallHistory(Console
, Console
->LineUpPressed
? -1 : 0);
504 Console
->LineUpPressed
= TRUE
;
507 /* Recall next history entry */
508 LineInputRecallHistory(Console
, +1);
511 /* Recall remainder of current history entry */
512 HistoryGetCurrentEntry(Console
, &Entry
);
513 if (Pos
* sizeof(WCHAR
) < Entry
.Length
)
515 UINT InsertSize
= (Entry
.Length
/ sizeof(WCHAR
) - Pos
);
516 UINT DeleteSize
= min(Console
->LineSize
- Pos
, InsertSize
);
517 LineInputEdit(Console
, DeleteSize
, InsertSize
, &Entry
.Buffer
[Pos
]);
521 /* Insert a ^Z character */
522 KeyEvent
->uChar
.UnicodeChar
= 26;
525 if (KeyEvent
->dwControlKeyState
& (LEFT_ALT_PRESSED
| RIGHT_ALT_PRESSED
))
526 HistoryDeleteBuffer(HistoryCurrentBuffer(Console
));
529 /* Search for history entries starting with input. */
530 if (!(Hist
= HistoryCurrentBuffer(Console
)) || Hist
->NumEntries
== 0)
533 /* Like Up/F5, on first time start from current (usually last) entry,
534 * but on subsequent times start at previous entry. */
535 if (Console
->LineUpPressed
)
536 Hist
->Position
= (Hist
->Position
? Hist
->Position
: Hist
->NumEntries
) - 1;
537 Console
->LineUpPressed
= TRUE
;
539 Entry
.Length
= Console
->LinePos
* sizeof(WCHAR
);
540 Entry
.Buffer
= Console
->LineBuffer
;
542 /* Keep going backwards, even wrapping around to the end,
543 * until we get back to starting point */
544 HistPos
= Hist
->Position
;
547 if (RtlPrefixUnicodeString(&Entry
, &Hist
->Entries
[HistPos
], FALSE
))
549 Hist
->Position
= HistPos
;
550 LineInputEdit(Console
, Console
->LineSize
- Pos
,
551 Hist
->Entries
[HistPos
].Length
/ sizeof(WCHAR
) - Pos
,
552 &Hist
->Entries
[HistPos
].Buffer
[Pos
]);
553 /* Cursor stays where it was */
554 LineInputSetPos(Console
, Pos
);
557 if (--HistPos
< 0) HistPos
+= Hist
->NumEntries
;
558 } while (HistPos
!= Hist
->Position
);
562 if (KeyEvent
->uChar
.UnicodeChar
== L
'\b' && Console
->Mode
& ENABLE_PROCESSED_INPUT
)
564 /* backspace handling - if processed input enabled then we handle it here
565 * otherwise we treat it like a normal char. */
568 LineInputSetPos(Console
, Pos
- 1);
569 LineInputEdit(Console
, 1, 0, NULL
);
572 else if (KeyEvent
->uChar
.UnicodeChar
== L
'\r')
574 HistoryAddEntry(Console
);
576 /* TODO: Expand aliases */
578 LineInputSetPos(Console
, Console
->LineSize
);
579 Console
->LineBuffer
[Console
->LineSize
++] = L
'\r';
580 if (Console
->Mode
& ENABLE_ECHO_INPUT
)
581 ConioWriteConsole(Console
, Console
->ActiveBuffer
, "\r", 1, TRUE
);
583 /* Add \n if processed input. There should usually be room for it,
584 * but an exception to the rule exists: the buffer could have been
585 * pre-filled with LineMaxSize - 1 characters. */
586 if (Console
->Mode
& ENABLE_PROCESSED_INPUT
&&
587 Console
->LineSize
< Console
->LineMaxSize
)
589 Console
->LineBuffer
[Console
->LineSize
++] = L
'\n';
590 if (Console
->Mode
& ENABLE_ECHO_INPUT
)
591 ConioWriteConsole(Console
, Console
->ActiveBuffer
, "\n", 1, TRUE
);
593 Console
->LineComplete
= TRUE
;
594 Console
->LinePos
= 0;
596 else if (KeyEvent
->uChar
.UnicodeChar
!= L
'\0')
598 if (KeyEvent
->uChar
.UnicodeChar
< 0x20 &&
599 Console
->LineWakeupMask
& (1 << KeyEvent
->uChar
.UnicodeChar
))
601 /* Control key client wants to handle itself (e.g. for tab completion) */
602 Console
->LineBuffer
[Console
->LineSize
++] = L
' ';
603 Console
->LineBuffer
[Console
->LinePos
] = KeyEvent
->uChar
.UnicodeChar
;
604 Console
->LineComplete
= TRUE
;
605 Console
->LinePos
= 0;
609 /* Normal character */
610 BOOL Overstrike
= Console
->LineInsertToggle
&& Console
->LinePos
!= Console
->LineSize
;
611 LineInputEdit(Console
, Overstrike
, 1, &KeyEvent
->uChar
.UnicodeChar
);