static void WCEL_SaveYank(WCEL_Context* ctx, int beg, int end) { int len = end - beg; if (len <= 0) return; WCEL_FreeYank(ctx); /* After WCEL_FreeYank ctx->yanked is empty */ ctx->yanked = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR)); if (!ctx->yanked) return; memcpy(ctx->yanked, &ctx->line[beg], len * sizeof(WCHAR)); ctx->yanked[len] = 0; }
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; }