static VOID HistoryGetCurrentEntry(PCSRSS_CONSOLE Console, PUNICODE_STRING Entry) { PHISTORY_BUFFER Hist; if (!(Hist = HistoryCurrentBuffer(Console)) || Hist->NumEntries == 0) Entry->Length = 0; else *Entry = Hist->Entries[Hist->Position]; }
static VOID HistoryAddEntry(PCSRSS_CONSOLE Console) { UNICODE_STRING NewEntry; PHISTORY_BUFFER Hist; INT i; NewEntry.Length = NewEntry.MaximumLength = Console->LineSize * sizeof(WCHAR); NewEntry.Buffer = Console->LineBuffer; if (!(Hist = HistoryCurrentBuffer(Console))) return; /* Don't add blank or duplicate entries */ if (NewEntry.Length == 0 || Hist->MaxEntries == 0 || (Hist->NumEntries > 0 && RtlEqualUnicodeString(&Hist->Entries[Hist->NumEntries - 1], &NewEntry, FALSE))) { return; } if (Console->HistoryNoDup) { /* Check if this line has been entered before */ for (i = Hist->NumEntries - 1; i >= 0; i--) { if (RtlEqualUnicodeString(&Hist->Entries[i], &NewEntry, FALSE)) { /* Just rotate the list to bring this entry to the end */ NewEntry = Hist->Entries[i]; memmove(&Hist->Entries[i], &Hist->Entries[i + 1], (Hist->NumEntries - (i + 1)) * sizeof(UNICODE_STRING)); Hist->Entries[Hist->NumEntries - 1] = NewEntry; Hist->Position = Hist->NumEntries - 1; return; } } } if (Hist->NumEntries == Hist->MaxEntries) { /* List is full, remove oldest entry */ RtlFreeUnicodeString(&Hist->Entries[0]); memmove(&Hist->Entries[0], &Hist->Entries[1], --Hist->NumEntries * sizeof(UNICODE_STRING)); } if (NT_SUCCESS(RtlDuplicateUnicodeString(0, &NewEntry, &Hist->Entries[Hist->NumEntries]))) Hist->NumEntries++; Hist->Position = Hist->NumEntries - 1; }
static VOID LineInputRecallHistory(PCONSOLE Console, INT Offset) { PHISTORY_BUFFER Hist = HistoryCurrentBuffer(Console); UINT Position = 0; if (!Hist || Hist->NumEntries == 0) return; Position = Hist->Position + Offset; Position = min(max(Position, 0), Hist->NumEntries - 1); Hist->Position = Position; LineInputSetPos(Console, 0); LineInputEdit(Console, Console->LineSize, Hist->Entries[Hist->Position].Length / sizeof(WCHAR), Hist->Entries[Hist->Position].Buffer); }
static VOID LineInputRecallHistory(PCSRSS_CONSOLE Console, INT Offset) { PHISTORY_BUFFER Hist; if (!(Hist = HistoryCurrentBuffer(Console)) || Hist->NumEntries == 0) return; Offset += Hist->Position; Offset = max(Offset, 0); Offset = min(Offset, Hist->NumEntries - 1); Hist->Position = Offset; LineInputSetPos(Console, 0); LineInputEdit(Console, Console->LineSize, Hist->Entries[Offset].Length / sizeof(WCHAR), Hist->Entries[Offset].Buffer); }
VOID FASTCALL LineInputKeyDown(PCSRSS_CONSOLE Console, KEY_EVENT_RECORD *KeyEvent) { UINT Pos = Console->LinePos; PHISTORY_BUFFER Hist; UNICODE_STRING Entry; INT HistPos; switch (KeyEvent->wVirtualKeyCode) { case VK_ESCAPE: /* Clear entire line */ LineInputSetPos(Console, 0); LineInputEdit(Console, Console->LineSize, 0, NULL); return; case VK_HOME: /* Move to start of line. With ctrl, erase everything left of cursor */ LineInputSetPos(Console, 0); if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) LineInputEdit(Console, Pos, 0, NULL); return; case VK_END: /* Move to end of line. With ctrl, erase everything right of cursor */ if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) LineInputEdit(Console, Console->LineSize - Pos, 0, NULL); else LineInputSetPos(Console, Console->LineSize); return; case VK_LEFT: /* Move left. With ctrl, move to beginning of previous word */ if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) { while (Pos > 0 && Console->LineBuffer[Pos - 1] == L' ') Pos--; while (Pos > 0 && Console->LineBuffer[Pos - 1] != L' ') Pos--; } else { Pos -= (Pos > 0); } LineInputSetPos(Console, Pos); return; case VK_RIGHT: case VK_F1: /* Move right. With ctrl, move to beginning of next word */ if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) { while (Pos < Console->LineSize && Console->LineBuffer[Pos] != L' ') Pos++; while (Pos < Console->LineSize && Console->LineBuffer[Pos] == L' ') Pos++; LineInputSetPos(Console, Pos); return; } else { /* Recall one character (but don't overwrite current line) */ HistoryGetCurrentEntry(Console, &Entry); if (Pos < Console->LineSize) LineInputSetPos(Console, Pos + 1); else if (Pos * sizeof(WCHAR) < Entry.Length) LineInputEdit(Console, 0, 1, &Entry.Buffer[Pos]); } return; case VK_INSERT: /* Toggle between insert and overstrike */ Console->LineInsertToggle = !Console->LineInsertToggle; ConioSetCursorInfo(Console, Console->ActiveBuffer); return; case VK_DELETE: /* Remove character to right of cursor */ if (Pos != Console->LineSize) LineInputEdit(Console, 1, 0, NULL); return; case VK_PRIOR: /* Recall first history entry */ LineInputRecallHistory(Console, -((WORD)-1)); return; case VK_NEXT: /* Recall last history entry */ LineInputRecallHistory(Console, +((WORD)-1)); return; case VK_UP: case VK_F5: /* Recall previous history entry. On first time, actually recall the * current (usually last) entry; on subsequent times go back. */ LineInputRecallHistory(Console, Console->LineUpPressed ? -1 : 0); Console->LineUpPressed = TRUE; return; case VK_DOWN: /* Recall next history entry */ LineInputRecallHistory(Console, +1); return; case VK_F3: /* Recall remainder of current history entry */ HistoryGetCurrentEntry(Console, &Entry); if (Pos * sizeof(WCHAR) < Entry.Length) { UINT InsertSize = (Entry.Length / sizeof(WCHAR) - Pos); UINT DeleteSize = min(Console->LineSize - Pos, InsertSize); LineInputEdit(Console, DeleteSize, InsertSize, &Entry.Buffer[Pos]); } return; case VK_F6: /* Insert a ^Z character */ KeyEvent->uChar.UnicodeChar = 26; break; case VK_F7: if (KeyEvent->dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) HistoryDeleteBuffer(HistoryCurrentBuffer(Console)); return; case VK_F8: /* Search for history entries starting with input. */ if (!(Hist = HistoryCurrentBuffer(Console)) || Hist->NumEntries == 0) return; /* Like Up/F5, on first time start from current (usually last) entry, * but on subsequent times start at previous entry. */ if (Console->LineUpPressed) Hist->Position = (Hist->Position ? Hist->Position : Hist->NumEntries) - 1; Console->LineUpPressed = TRUE; Entry.Length = Console->LinePos * sizeof(WCHAR); Entry.Buffer = Console->LineBuffer; /* Keep going backwards, even wrapping around to the end, * until we get back to starting point */ HistPos = Hist->Position; do { if (RtlPrefixUnicodeString(&Entry, &Hist->Entries[HistPos], FALSE)) { Hist->Position = HistPos; LineInputEdit(Console, Console->LineSize - Pos, Hist->Entries[HistPos].Length / sizeof(WCHAR) - Pos, &Hist->Entries[HistPos].Buffer[Pos]); /* Cursor stays where it was */ LineInputSetPos(Console, Pos); return; } if (--HistPos < 0) HistPos += Hist->NumEntries; } while (HistPos != Hist->Position); return; } if (KeyEvent->uChar.UnicodeChar == L'\b' && Console->Mode & ENABLE_PROCESSED_INPUT) { /* backspace handling - if processed input enabled then we handle it here * otherwise we treat it like a normal char. */ if (Pos > 0) { LineInputSetPos(Console, Pos - 1); LineInputEdit(Console, 1, 0, NULL); } } else if (KeyEvent->uChar.UnicodeChar == L'\r') { HistoryAddEntry(Console); /* TODO: Expand aliases */ LineInputSetPos(Console, Console->LineSize); Console->LineBuffer[Console->LineSize++] = L'\r'; if (Console->Mode & ENABLE_ECHO_INPUT) ConioWriteConsole(Console, Console->ActiveBuffer, "\r", 1, TRUE); /* Add \n if processed input. There should usually be room for it, * but an exception to the rule exists: the buffer could have been * pre-filled with LineMaxSize - 1 characters. */ if (Console->Mode & ENABLE_PROCESSED_INPUT && Console->LineSize < Console->LineMaxSize) { Console->LineBuffer[Console->LineSize++] = L'\n'; if (Console->Mode & ENABLE_ECHO_INPUT) ConioWriteConsole(Console, Console->ActiveBuffer, "\n", 1, TRUE); } Console->LineComplete = TRUE; Console->LinePos = 0; } else if (KeyEvent->uChar.UnicodeChar != L'\0') { if (KeyEvent->uChar.UnicodeChar < 0x20 && Console->LineWakeupMask & (1 << KeyEvent->uChar.UnicodeChar)) { /* Control key client wants to handle itself (e.g. for tab completion) */ Console->LineBuffer[Console->LineSize++] = L' '; Console->LineBuffer[Console->LinePos] = KeyEvent->uChar.UnicodeChar; Console->LineComplete = TRUE; Console->LinePos = 0; } else { /* Normal character */ BOOL Overstrike = Console->LineInsertToggle && Console->LinePos != Console->LineSize; LineInputEdit(Console, Overstrike, 1, &KeyEvent->uChar.UnicodeChar); } } }
VOID LineInputKeyDown(PCONSRV_CONSOLE Console, PUNICODE_STRING ExeName, KEY_EVENT_RECORD *KeyEvent) { UINT Pos = Console->LinePos; UNICODE_STRING Entry; /* * First, deal with control keys... */ switch (KeyEvent->wVirtualKeyCode) { case VK_ESCAPE: { /* Clear entire line */ LineInputSetPos(Console, 0); LineInputEdit(Console, Console->LineSize, 0, NULL); // TESTS!! if (Popup) { DestroyPopupWindow(Popup); Popup = NULL; } return; } case VK_HOME: { /* Move to start of line. With CTRL, erase everything left of cursor */ LineInputSetPos(Console, 0); if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) LineInputEdit(Console, Pos, 0, NULL); return; } case VK_END: { /* Move to end of line. With CTRL, erase everything right of cursor */ if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) LineInputEdit(Console, Console->LineSize - Pos, 0, NULL); else LineInputSetPos(Console, Console->LineSize); return; } case VK_LEFT: { /* Move left. With CTRL, move to beginning of previous word */ if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) { while (Pos > 0 && Console->LineBuffer[Pos - 1] == L' ') Pos--; while (Pos > 0 && Console->LineBuffer[Pos - 1] != L' ') Pos--; } else { Pos -= (Pos > 0); } LineInputSetPos(Console, Pos); return; } case VK_RIGHT: case VK_F1: { /* Move right. With CTRL, move to beginning of next word */ if (KeyEvent->dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) { while (Pos < Console->LineSize && Console->LineBuffer[Pos] != L' ') Pos++; while (Pos < Console->LineSize && Console->LineBuffer[Pos] == L' ') Pos++; LineInputSetPos(Console, Pos); } else { /* Recall one character (but don't overwrite current line) */ HistoryGetCurrentEntry(Console, ExeName, &Entry); if (Pos < Console->LineSize) LineInputSetPos(Console, Pos + 1); else if (Pos * sizeof(WCHAR) < Entry.Length) LineInputEdit(Console, 0, 1, &Entry.Buffer[Pos]); } return; } case VK_INSERT: { /* Toggle between insert and overstrike */ Console->LineInsertToggle = !Console->LineInsertToggle; TermSetCursorInfo(Console, Console->ActiveBuffer); return; } case VK_DELETE: { /* Remove character to right of cursor */ if (Pos != Console->LineSize) LineInputEdit(Console, 1, 0, NULL); return; } case VK_PRIOR: { /* Recall first history entry */ LineInputRecallHistory(Console, ExeName, -((WORD)-1)); return; } case VK_NEXT: { /* Recall last history entry */ LineInputRecallHistory(Console, ExeName, +((WORD)-1)); return; } case VK_UP: case VK_F5: { /* * Recall previous history entry. On first time, actually recall the * current (usually last) entry; on subsequent times go back. */ LineInputRecallHistory(Console, ExeName, Console->LineUpPressed ? -1 : 0); Console->LineUpPressed = TRUE; return; } case VK_DOWN: { /* Recall next history entry */ LineInputRecallHistory(Console, ExeName, +1); return; } case VK_F3: { /* Recall remainder of current history entry */ HistoryGetCurrentEntry(Console, ExeName, &Entry); if (Pos * sizeof(WCHAR) < Entry.Length) { UINT InsertSize = (Entry.Length / sizeof(WCHAR) - Pos); UINT DeleteSize = min(Console->LineSize - Pos, InsertSize); LineInputEdit(Console, DeleteSize, InsertSize, &Entry.Buffer[Pos]); } return; } case VK_F6: { /* Insert a ^Z character */ KeyEvent->uChar.UnicodeChar = 26; break; } case VK_F7: { if (KeyEvent->dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) HistoryDeleteCurrentBuffer(Console, ExeName); else { if (Popup) DestroyPopupWindow(Popup); Popup = HistoryDisplayCurrentHistory(Console, ExeName); } return; } case VK_F8: { UNICODE_STRING EntryFound; Entry.Length = Console->LinePos * sizeof(WCHAR); // == Pos * sizeof(WCHAR) Entry.Buffer = Console->LineBuffer; if (HistoryFindEntryByPrefix(Console, ExeName, &Entry, &EntryFound)) { LineInputEdit(Console, Console->LineSize - Pos, EntryFound.Length / sizeof(WCHAR) - Pos, &EntryFound.Buffer[Pos]); /* Cursor stays where it was */ LineInputSetPos(Console, Pos); } return; } #if 0 { PHISTORY_BUFFER Hist; INT HistPos; /* Search for history entries starting with input. */ Hist = HistoryCurrentBuffer(Console, ExeName); if (!Hist || Hist->NumEntries == 0) return; /* * Like Up/F5, on first time start from current (usually last) entry, * but on subsequent times start at previous entry. */ if (Console->LineUpPressed) Hist->Position = (Hist->Position ? Hist->Position : Hist->NumEntries) - 1; Console->LineUpPressed = TRUE; Entry.Length = Console->LinePos * sizeof(WCHAR); // == Pos * sizeof(WCHAR) Entry.Buffer = Console->LineBuffer; /* * Keep going backwards, even wrapping around to the end, * until we get back to starting point. */ HistPos = Hist->Position; do { if (RtlPrefixUnicodeString(&Entry, &Hist->Entries[HistPos], FALSE)) { Hist->Position = HistPos; LineInputEdit(Console, Console->LineSize - Pos, Hist->Entries[HistPos].Length / sizeof(WCHAR) - Pos, &Hist->Entries[HistPos].Buffer[Pos]); /* Cursor stays where it was */ LineInputSetPos(Console, Pos); return; } if (--HistPos < 0) HistPos += Hist->NumEntries; } while (HistPos != Hist->Position); return; } #endif return; } /* * OK, we deal with normal keys, we can continue... */ if (KeyEvent->uChar.UnicodeChar == L'\b' && GetConsoleInputBufferMode(Console) & ENABLE_PROCESSED_INPUT) { /* backspace handling - if processed input enabled then we handle it here * otherwise we treat it like a normal char. */ if (Pos > 0) { LineInputSetPos(Console, Pos - 1); LineInputEdit(Console, 1, 0, NULL); } } else if (KeyEvent->uChar.UnicodeChar == L'\r') { Entry.Length = Entry.MaximumLength = Console->LineSize * sizeof(WCHAR); Entry.Buffer = Console->LineBuffer; HistoryAddEntry(Console, ExeName, &Entry); /* TODO: Expand aliases */ DPRINT1("TODO: Expand aliases\n"); LineInputSetPos(Console, Console->LineSize); Console->LineBuffer[Console->LineSize++] = L'\r'; if (GetConsoleInputBufferMode(Console) & ENABLE_ECHO_INPUT) { if (GetType(Console->ActiveBuffer) == TEXTMODE_BUFFER) { TermWriteStream(Console, (PTEXTMODE_SCREEN_BUFFER)(Console->ActiveBuffer), L"\r", 1, TRUE); } } /* * Add \n if processed input. There should usually be room for it, * but an exception to the rule exists: the buffer could have been * pre-filled with LineMaxSize - 1 characters. */ if (GetConsoleInputBufferMode(Console) & ENABLE_PROCESSED_INPUT && Console->LineSize < Console->LineMaxSize) { Console->LineBuffer[Console->LineSize++] = L'\n'; if (GetConsoleInputBufferMode(Console) & ENABLE_ECHO_INPUT) { if (GetType(Console->ActiveBuffer) == TEXTMODE_BUFFER) { TermWriteStream(Console, (PTEXTMODE_SCREEN_BUFFER)(Console->ActiveBuffer), L"\n", 1, TRUE); } } } Console->LineComplete = TRUE; Console->LinePos = 0; } else if (KeyEvent->uChar.UnicodeChar != L'\0') { if (KeyEvent->uChar.UnicodeChar < 0x20 && Console->LineWakeupMask & (1 << KeyEvent->uChar.UnicodeChar)) { /* Control key client wants to handle itself (e.g. for tab completion) */ Console->LineBuffer[Console->LineSize++] = L' '; Console->LineBuffer[Console->LinePos] = KeyEvent->uChar.UnicodeChar; Console->LineComplete = TRUE; Console->LinePos = 0; } else { /* Normal character */ BOOL Overstrike = !Console->LineInsertToggle && (Console->LinePos != Console->LineSize); DPRINT("Overstrike = %s\n", Overstrike ? "true" : "false"); LineInputEdit(Console, (Overstrike ? 1 : 0), 1, &KeyEvent->uChar.UnicodeChar); } } }