static inline void WCEL_Update(WCEL_Context* ctx, int beg, int len) { WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[beg], len, WCEL_GetCoord(ctx, beg), NULL); FillConsoleOutputAttribute(ctx->hConOut, ctx->csbi.wAttributes, len, WCEL_GetCoord(ctx, beg), NULL); }
static void WCEL_Redraw(WCEL_Context* ctx) { COORD c = WCEL_GetCoord(ctx, ctx->len); CHAR_INFO ci; WCEL_Update(ctx, 0, ctx->len); ci.Char.UnicodeChar = ' '; ci.Attributes = ctx->csbi.wAttributes; CONSOLE_FillLineUniform(ctx->hConOut, c.X, c.Y, ctx->csbi.dwSize.X - c.X, &ci); }
static void WCEL_DeleteString(WCEL_Context* ctx, int beg, int end) { unsigned str_len = end - beg; if (end < ctx->len) memmove(&ctx->line[beg], &ctx->line[end], (ctx->len - end) * sizeof(WCHAR)); /* we need to clean from ctx->len - str_len to ctx->len */ if (ctx->shall_echo) { COORD cbeg = WCEL_GetCoord(ctx, ctx->len - str_len); COORD cend = WCEL_GetCoord(ctx, ctx->len); CHAR_INFO ci; ci.Char.UnicodeChar = ' '; ci.Attributes = ctx->csbi.wAttributes; if (cbeg.Y == cend.Y) { /* partial erase of sole line */ CONSOLE_FillLineUniform(ctx->hConOut, cbeg.X, cbeg.Y, cend.X - cbeg.X, &ci); } else { int i; /* erase til eol on first line */ CONSOLE_FillLineUniform(ctx->hConOut, cbeg.X, cbeg.Y, ctx->csbi.dwSize.X - cbeg.X, &ci); /* completely erase all the others (full lines) */ for (i = cbeg.Y + 1; i < cend.Y; i++) CONSOLE_FillLineUniform(ctx->hConOut, 0, i, ctx->csbi.dwSize.X, &ci); /* erase from beginning of line until last pos on last line */ CONSOLE_FillLineUniform(ctx->hConOut, 0, cend.Y, cend.X, &ci); } } ctx->len -= str_len; WCEL_Update(ctx, 0, ctx->len); ctx->line[ctx->len] = 0; }
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 inline void WCEL_Update(WCEL_Context* ctx, int beg, int len) { int i, last; DWORD count; WCHAR tmp[2]; /* bare console case is handled in CONSOLE_ReadLine (we always reprint the whole string) */ if (!ctx->shall_echo || !ctx->can_pos_cursor) return; for (i = last = beg; i < beg + len; i++) { if (ctx->line[i] < ' ') { if (last != i) { WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[last], i - last, WCEL_GetCoord(ctx, last), &count); FillConsoleOutputAttribute(ctx->hConOut, ctx->csbi.wAttributes, i - last, WCEL_GetCoord(ctx, last), &count); } tmp[0] = '^'; tmp[1] = '@' + ctx->line[i]; WriteConsoleOutputCharacterW(ctx->hConOut, tmp, 2, WCEL_GetCoord(ctx, i), &count); FillConsoleOutputAttribute(ctx->hConOut, ctx->csbi.wAttributes, 2, WCEL_GetCoord(ctx, i), &count); last = i + 1; } } if (last != beg + len) { WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[last], i - last, WCEL_GetCoord(ctx, last), &count); FillConsoleOutputAttribute(ctx->hConOut, ctx->csbi.wAttributes, i - last, WCEL_GetCoord(ctx, last), &count); } }
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; }