static void WCEL_InsertString(WCEL_Context* ctx, const WCHAR* str) { size_t len = lstrlenW(str), updtlen; if (!len) return; if (ctx->insert) { if (!WCEL_Grow(ctx, len)) return; if (ctx->len > ctx->ofs) memmove(&ctx->line[ctx->ofs + len], &ctx->line[ctx->ofs], (ctx->len - ctx->ofs) * sizeof(WCHAR)); ctx->len += len; updtlen = ctx->len - ctx->ofs; } else { if (ctx->ofs + len > ctx->len) { if (!WCEL_Grow(ctx, (ctx->ofs + len) - ctx->len)) return; ctx->len = ctx->ofs + len; } updtlen = len; } memcpy(&ctx->line[ctx->ofs], str, len * sizeof(WCHAR)); ctx->line[ctx->len] = 0; WCEL_Update(ctx, ctx->ofs, updtlen); ctx->ofs += len; }
static void WCEL_Done(WCEL_Context* ctx) { WCHAR nl = '\n'; if (!WCEL_Grow(ctx, 2)) return; ctx->line[ctx->len++] = '\r'; ctx->line[ctx->len++] = '\n'; ctx->line[ctx->len] = 0; WriteConsoleW(ctx->hConOut, &nl, 1, NULL, NULL); ctx->done = 1; }
static void WCEL_Done(WCEL_Context* ctx) { WCHAR nl = '\n'; if (!WCEL_Grow(ctx, 2)) return; ctx->line[ctx->len++] = '\r'; ctx->line[ctx->len++] = '\n'; ctx->line[ctx->len] = 0; WriteConsoleW(ctx->hConOut, &nl, 1, NULL, NULL); if (ctx->insertkey) SetConsoleCursorInfo(ctx->hConOut, &ctx->cinfo); ctx->done = 1; }
static void WCEL_FindPrevInHist(WCEL_Context* ctx) { int startPos = ctx->histPos; WCHAR* data; unsigned int len, oldofs; if (ctx->histPos && ctx->histPos == ctx->histSize) { startPos--; ctx->histPos--; } do { data = WCEL_GetHistory(ctx, ctx->histPos); if (ctx->histPos) ctx->histPos--; else ctx->histPos = (ctx->histSize-1); len = lstrlenW(data) + 1; if ((len >= ctx->ofs) && (memcmp(ctx->line, data, ctx->ofs * sizeof(WCHAR)) == 0)) { /* need to clean also the screen if new string is shorter than old one */ WCEL_DeleteString(ctx, 0, ctx->len); if (WCEL_Grow(ctx, len)) { oldofs = ctx->ofs; ctx->ofs = 0; WCEL_InsertString(ctx, data); ctx->ofs = oldofs; if (ctx->shall_echo) SetConsoleCursorPosition(ctx->hConOut, WCEL_GetCoord(ctx, ctx->ofs)); HeapFree(GetProcessHeap(), 0, data); return; } } } while (ctx->histPos != startPos); return; }
static void WCEL_MoveToHist(WCEL_Context* ctx, int idx) { WCHAR* data = WCEL_GetHistory(ctx, idx); int len = lstrlenW(data) + 1; /* save current line edition for recall when needed (FIXME seems broken to me) */ if (ctx->histPos == ctx->histSize - 1) { HeapFree(GetProcessHeap(), 0, ctx->histCurr); ctx->histCurr = HeapAlloc(GetProcessHeap(), 0, (ctx->len + 1) * sizeof(WCHAR)); memcpy(ctx->histCurr, ctx->line, (ctx->len + 1) * sizeof(WCHAR)); } /* need to clean also the screen if new string is shorter than old one */ WCEL_DeleteString(ctx, 0, ctx->len); ctx->ofs = 0; /* insert new string */ if (WCEL_Grow(ctx, len)) { WCEL_InsertString(ctx, data); HeapFree(GetProcessHeap(), 0, data); ctx->histPos = idx; } }
WCHAR* CONSOLE_Readline(HANDLE hConsoleIn) { WCEL_Context ctx; INPUT_RECORD ir; const KeyMap* km; const KeyEntry* ke; unsigned ofs; void (*func)(struct WCEL_Context* ctx); DWORD ks; int use_emacs; memset(&ctx, 0, sizeof(ctx)); ctx.hConIn = hConsoleIn; WCEL_HistoryInit(&ctx); if (!CONSOLE_GetEditionMode(hConsoleIn, &use_emacs)) use_emacs = 0; if ((ctx.hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE || !GetConsoleScreenBufferInfo(ctx.hConOut, &ctx.csbi)) return NULL; ctx.can_wrap = (GetConsoleMode(ctx.hConOut, &ks) && (ks & ENABLE_WRAP_AT_EOL_OUTPUT)) ? 1 : 0; if (!WCEL_Grow(&ctx, 1)) { CloseHandle(ctx.hConOut); return NULL; } ctx.line[0] = 0; /* EPP WCEL_Dump(&ctx, "init"); */ while (!ctx.done && !ctx.error && WCEL_Get(&ctx, &ir)) { if (ir.EventType != KEY_EVENT) continue; TRACE("key%s repeatCount=%u, keyCode=%02x scanCode=%02x char=%02x keyState=%08x\n", ir.Event.KeyEvent.bKeyDown ? "Down" : "Up ", ir.Event.KeyEvent.wRepeatCount, ir.Event.KeyEvent.wVirtualKeyCode, ir.Event.KeyEvent.wVirtualScanCode, ir.Event.KeyEvent.uChar.UnicodeChar, ir.Event.KeyEvent.dwControlKeyState); if (!ir.Event.KeyEvent.bKeyDown) continue; /* EPP WCEL_Dump(&ctx, "before func"); */ ofs = ctx.ofs; /* mask out some bits which don't interest us */ ks = ir.Event.KeyEvent.dwControlKeyState & ~(NUMLOCK_ON|SCROLLLOCK_ON|CAPSLOCK_ON|ENHANCED_KEY); func = NULL; for (km = (use_emacs) ? EmacsKeyMap : Win32KeyMap; km->entries != NULL; km++) { if (km->keyState != ks) continue; if (km->chkChar) { for (ke = &km->entries[0]; ke->func != 0; ke++) if (ke->val == ir.Event.KeyEvent.uChar.UnicodeChar) break; } else { for (ke = &km->entries[0]; ke->func != 0; ke++) if (ke->val == ir.Event.KeyEvent.wVirtualKeyCode) break; } if (ke->func) { func = ke->func; break; } } if (func) (func)(&ctx); else if (!(ir.Event.KeyEvent.dwControlKeyState & LEFT_ALT_PRESSED)) WCEL_InsertChar(&ctx, ir.Event.KeyEvent.uChar.UnicodeChar); else TRACE("Dropped event\n"); /* EPP WCEL_Dump(&ctx, "after func"); */ if (ctx.ofs != ofs) SetConsoleCursorPosition(ctx.hConOut, WCEL_GetCoord(&ctx, ctx.ofs)); } if (ctx.error) { HeapFree(GetProcessHeap(), 0, ctx.line); ctx.line = NULL; } WCEL_FreeYank(&ctx); if (ctx.line) CONSOLE_AppendHistory(ctx.line); CloseHandle(ctx.hConOut); HeapFree(GetProcessHeap(), 0, ctx.histCurr); return ctx.line; }
WCHAR* CONSOLE_Readline(HANDLE hConsoleIn, BOOL can_pos_cursor) { WCEL_Context ctx; INPUT_RECORD ir; const KeyMap* km; const KeyEntry* ke; unsigned ofs; void (*func)(struct WCEL_Context* ctx); DWORD mode, input_mode, ks; int use_emacs; memset(&ctx, 0, sizeof(ctx)); ctx.hConIn = hConsoleIn; WCEL_HistoryInit(&ctx); if (!CONSOLE_GetEditionMode(hConsoleIn, &use_emacs)) use_emacs = 0; if ((ctx.hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE || !GetConsoleScreenBufferInfo(ctx.hConOut, &ctx.csbi)) return NULL; if (!GetConsoleMode(hConsoleIn, &mode)) mode = 0; input_mode = mode; ctx.shall_echo = (mode & ENABLE_ECHO_INPUT) ? 1 : 0; ctx.insert = (mode & (ENABLE_INSERT_MODE|ENABLE_EXTENDED_FLAGS)) == (ENABLE_INSERT_MODE|ENABLE_EXTENDED_FLAGS) ? 1 : 0; if (!GetConsoleMode(ctx.hConOut, &mode)) mode = 0; ctx.can_wrap = (mode & ENABLE_WRAP_AT_EOL_OUTPUT) ? 1 : 0; ctx.can_pos_cursor = can_pos_cursor; GetConsoleCursorInfo(ctx.hConOut, &ctx.cinfo); if (!WCEL_Grow(&ctx, 1)) { CloseHandle(ctx.hConOut); return NULL; } ctx.line[0] = 0; /* EPP WCEL_Dump(&ctx, "init"); */ while (!ctx.done && !ctx.error && WCEL_Get(&ctx, &ir)) { if (ir.EventType != KEY_EVENT) continue; TRACE("key%s repeatCount=%u, keyCode=%02x scanCode=%02x char=%02x keyState=%08x\n", ir.Event.KeyEvent.bKeyDown ? "Down" : "Up ", ir.Event.KeyEvent.wRepeatCount, ir.Event.KeyEvent.wVirtualKeyCode, ir.Event.KeyEvent.wVirtualScanCode, ir.Event.KeyEvent.uChar.UnicodeChar, ir.Event.KeyEvent.dwControlKeyState); if (!ir.Event.KeyEvent.bKeyDown) continue; /* EPP WCEL_Dump(&ctx, "before func"); */ ofs = ctx.ofs; /* mask out some bits which don't interest us */ ks = ir.Event.KeyEvent.dwControlKeyState & ~(NUMLOCK_ON|SCROLLLOCK_ON|CAPSLOCK_ON|ENHANCED_KEY); func = NULL; for (km = (use_emacs) ? EmacsKeyMap : Win32KeyMap; km->entries != NULL; km++) { if (km->keyState != ks) continue; if (km->chkChar) { for (ke = &km->entries[0]; ke->func != 0; ke++) if (ke->val == ir.Event.KeyEvent.uChar.UnicodeChar) break; } else { for (ke = &km->entries[0]; ke->func != 0; ke++) if (ke->val == ir.Event.KeyEvent.wVirtualKeyCode) break; } if (ke->func) { func = ke->func; break; } } GetConsoleMode(hConsoleIn, &mode); if (input_mode != mode) { input_mode = mode; ctx.insertkey = 0; } ctx.insert = (mode & (ENABLE_INSERT_MODE|ENABLE_EXTENDED_FLAGS)) == (ENABLE_INSERT_MODE|ENABLE_EXTENDED_FLAGS); if (ctx.insertkey) ctx.insert = !ctx.insert; if (func) (func)(&ctx); else if (!(ir.Event.KeyEvent.dwControlKeyState & LEFT_ALT_PRESSED)) WCEL_InsertChar(&ctx, ir.Event.KeyEvent.uChar.UnicodeChar); else TRACE("Dropped event\n"); /* EPP WCEL_Dump(&ctx, "after func"); */ if (!ctx.shall_echo) continue; if (ctx.can_pos_cursor) { if (ctx.ofs != ofs) SetConsoleCursorPosition(ctx.hConOut, WCEL_GetCoord(&ctx, ctx.ofs)); } else if (!ctx.done && !ctx.error) { DWORD last; /* erase previous chars */ WCEL_WriteNChars(&ctx, '\b', ctx.last_rub); /* write chars up to cursor */ ctx.last_rub = WCEL_WriteConsole(&ctx, 0, ctx.ofs); /* write chars past cursor */ last = ctx.last_rub + WCEL_WriteConsole(&ctx, ctx.ofs, ctx.len - ctx.ofs); if (last < ctx.last_max) /* ctx.line has been shortened, erase */ { WCEL_WriteNChars(&ctx, ' ', ctx.last_max - last); WCEL_WriteNChars(&ctx, '\b', ctx.last_max - last); ctx.last_max = last; } else ctx.last_max = last; /* reposition at cursor */ WCEL_WriteNChars(&ctx, '\b', last - ctx.last_rub); } } if (ctx.error) { HeapFree(GetProcessHeap(), 0, ctx.line); ctx.line = NULL; } WCEL_FreeYank(&ctx); if (ctx.line) CONSOLE_AppendHistory(ctx.line); CloseHandle(ctx.hConOut); HeapFree(GetProcessHeap(), 0, ctx.histCurr); return ctx.line; }