Eric Pouech | 0b83d4c | 2001-11-23 23:04:58 +0000 | [diff] [blame] | 1 | /* |
| 2 | * line edition function for Win32 console |
| 3 | * |
| 4 | * Copyright 2001 Eric Pouech |
Alexandre Julliard | 0799c1a | 2002-03-09 23:29:33 +0000 | [diff] [blame] | 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Lesser General Public |
| 8 | * License as published by the Free Software Foundation; either |
| 9 | * version 2.1 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * This library is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Lesser General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Lesser General Public |
| 17 | * License along with this library; if not, write to the Free Software |
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
Eric Pouech | 0b83d4c | 2001-11-23 23:04:58 +0000 | [diff] [blame] | 19 | */ |
| 20 | |
| 21 | #include "config.h" |
Alexandre Julliard | 5769d1d | 2002-04-26 19:05:15 +0000 | [diff] [blame^] | 22 | #include "wine/port.h" |
| 23 | |
Eric Pouech | 0b83d4c | 2001-11-23 23:04:58 +0000 | [diff] [blame] | 24 | #include <string.h> |
| 25 | |
| 26 | #include "windef.h" |
| 27 | #include "winbase.h" |
| 28 | #include "wincon.h" |
| 29 | #include "wine/unicode.h" |
| 30 | #include "winnls.h" |
Alexandre Julliard | 0799c1a | 2002-03-09 23:29:33 +0000 | [diff] [blame] | 31 | #include "wine/debug.h" |
Eric Pouech | 0b83d4c | 2001-11-23 23:04:58 +0000 | [diff] [blame] | 32 | |
Alexandre Julliard | 0799c1a | 2002-03-09 23:29:33 +0000 | [diff] [blame] | 33 | WINE_DEFAULT_DEBUG_CHANNEL(console); |
Eric Pouech | 0b83d4c | 2001-11-23 23:04:58 +0000 | [diff] [blame] | 34 | |
| 35 | /* console.c */ |
| 36 | extern int CONSOLE_GetHistory(int idx, WCHAR* buf, int buf_len); |
| 37 | extern BOOL CONSOLE_AppendHistory(const WCHAR *p); |
| 38 | extern unsigned int CONSOLE_GetNumHistoryEntries(void); |
| 39 | |
| 40 | struct WCEL_Context; |
| 41 | |
| 42 | typedef struct |
| 43 | { |
| 44 | WCHAR val; /* vk or unicode char */ |
| 45 | void (*func)(struct WCEL_Context* ctx); |
| 46 | } KeyEntry; |
| 47 | |
| 48 | typedef struct |
| 49 | { |
| 50 | DWORD keyState; /* keyState (from INPUT_RECORD) to match */ |
| 51 | BOOL chkChar; /* check vk or char */ |
| 52 | KeyEntry* entries; /* array of entries */ |
| 53 | } KeyMap; |
| 54 | |
| 55 | typedef struct WCEL_Context { |
| 56 | WCHAR* line; /* the line being edited */ |
| 57 | size_t alloc; /* number of WCHAR in line */ |
| 58 | unsigned len; /* number of chars in line */ |
| 59 | unsigned ofs; /* offset for cursor in current line */ |
| 60 | WCHAR* yanked; /* yanked line */ |
| 61 | unsigned mark; /* marked point (emacs mode only) */ |
| 62 | CONSOLE_SCREEN_BUFFER_INFO csbi; /* current state (initial cursor, window size, attribute) */ |
| 63 | HANDLE hConIn; |
| 64 | HANDLE hConOut; |
| 65 | unsigned done : 1, /* to 1 when we're done with editing */ |
| 66 | error : 1; /* to 1 when an error occurred in the editing */ |
| 67 | unsigned histSize; |
| 68 | unsigned histPos; |
| 69 | WCHAR* histCurr; |
| 70 | } WCEL_Context; |
| 71 | |
| 72 | #if 0 |
| 73 | static void WCEL_Dump(WCEL_Context* ctx, const char* pfx) |
| 74 | { |
| 75 | MESSAGE("%s: [line=%s[alloc=%u] ofs=%u len=%u start=(%d,%d) mask=%c%c\n" |
| 76 | "\t\thist=(size=%u pos=%u curr=%s)\n", |
| 77 | pfx, debugstr_w(ctx->line), ctx->alloc, ctx->ofs, ctx->len, |
| 78 | ctx->csbi.dwCursorPosition.X, ctx->csbi.dwCursorPosition.Y, |
| 79 | ctx->done ? 'D' : 'd', ctx->error ? 'E' : 'e', |
| 80 | ctx->histSize, ctx->histPos, debugstr_w(ctx->histCurr)); |
| 81 | } |
| 82 | #endif |
| 83 | |
| 84 | /* ==================================================================== |
| 85 | * |
| 86 | * Console helper functions |
| 87 | * |
| 88 | * ====================================================================*/ |
| 89 | |
| 90 | static BOOL WCEL_Get(WCEL_Context* ctx, INPUT_RECORD* ir) |
| 91 | { |
| 92 | DWORD retv; |
| 93 | |
| 94 | for (;;) |
| 95 | { |
| 96 | /* data available ? */ |
| 97 | if (ReadConsoleInputW(ctx->hConIn, ir, 1, &retv) && retv == 1) |
| 98 | return TRUE; |
| 99 | /* then wait... */ |
| 100 | switch (WaitForSingleObject(ctx->hConIn, INFINITE)) |
| 101 | { |
| 102 | case WAIT_OBJECT_0: |
| 103 | break; |
| 104 | default: |
| 105 | /* we have checked that hConIn was a console handle (could be sb) */ |
| 106 | ERR("Shouldn't happen\n"); |
| 107 | /* fall thru */ |
| 108 | case WAIT_ABANDONED: |
| 109 | case WAIT_TIMEOUT: |
| 110 | ctx->error = 1; |
| 111 | ERR("hmm bad situation\n"); |
| 112 | return FALSE; |
| 113 | } |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | static inline void WCEL_Beep(WCEL_Context* ctx) |
| 118 | { |
| 119 | Beep(400, 300); |
| 120 | } |
| 121 | |
| 122 | static inline COORD WCEL_GetCoord(WCEL_Context* ctx, int ofs) |
| 123 | { |
| 124 | COORD c; |
| 125 | c.X = ctx->csbi.dwCursorPosition.X + ofs; |
| 126 | c.Y = ctx->csbi.dwCursorPosition.Y; |
| 127 | return c; |
| 128 | } |
| 129 | |
| 130 | static inline void WCEL_GetRect(WCEL_Context* ctx, LPSMALL_RECT sr, int beg, int end) |
| 131 | { |
| 132 | sr->Left = ctx->csbi.dwCursorPosition.X + beg; |
| 133 | sr->Top = ctx->csbi.dwCursorPosition.Y; |
| 134 | sr->Right = ctx->csbi.dwCursorPosition.X + end; |
| 135 | sr->Bottom = ctx->csbi.dwCursorPosition.Y; |
| 136 | } |
| 137 | |
| 138 | /* ==================================================================== |
| 139 | * |
| 140 | * context manipulation functions |
| 141 | * |
| 142 | * ====================================================================*/ |
| 143 | |
| 144 | static BOOL WCEL_Grow(WCEL_Context* ctx, size_t len) |
| 145 | { |
| 146 | if (ctx->csbi.dwCursorPosition.X + ctx->ofs + len >= ctx->csbi.dwSize.X) |
| 147 | { |
| 148 | FIXME("Current implementation doesn't allow edition to spray across several lines\n"); |
| 149 | return FALSE; |
| 150 | } |
| 151 | |
| 152 | if (ctx->len + len >= ctx->alloc) |
| 153 | { |
| 154 | WCHAR* newline; |
| 155 | newline = HeapReAlloc(GetProcessHeap(), 0, ctx->line, sizeof(WCHAR) * (ctx->alloc + 32)); |
| 156 | if (!newline) return FALSE; |
| 157 | ctx->line = newline; |
| 158 | ctx->alloc += 32; |
| 159 | } |
| 160 | return TRUE; |
| 161 | } |
| 162 | |
| 163 | static void WCEL_DeleteString(WCEL_Context* ctx, int beg, int end) |
| 164 | { |
| 165 | SMALL_RECT scl, clp; |
| 166 | CHAR_INFO ci; |
| 167 | |
| 168 | if (end < ctx->len) |
| 169 | memmove(&ctx->line[beg], &ctx->line[end], (ctx->len - end) * sizeof(WCHAR)); |
| 170 | /* make the source rect bigger than the actual rect to that the part outside the clip |
| 171 | * rect (before the scroll) will get redrawn after the scroll |
| 172 | */ |
| 173 | WCEL_GetRect(ctx, &scl, end, ctx->len + end - beg); |
| 174 | WCEL_GetRect(ctx, &clp, beg, ctx->len); |
| 175 | |
| 176 | ci.Char.UnicodeChar = ' '; |
| 177 | ci.Attributes = ctx->csbi.wAttributes; |
| 178 | ScrollConsoleScreenBufferW(ctx->hConOut, &scl, &clp, WCEL_GetCoord(ctx, beg), &ci); |
| 179 | |
| 180 | ctx->len -= end - beg; |
| 181 | ctx->line[ctx->len] = 0; |
| 182 | } |
| 183 | |
| 184 | static void WCEL_InsertString(WCEL_Context* ctx, const WCHAR* str) |
| 185 | { |
| 186 | size_t len = lstrlenW(str); |
| 187 | |
| 188 | if (!len || !WCEL_Grow(ctx, len)) return; |
| 189 | if (ctx->len > ctx->ofs) |
| 190 | memmove(&ctx->line[ctx->ofs + len], &ctx->line[ctx->ofs], (ctx->len - ctx->ofs) * sizeof(WCHAR)); |
| 191 | memcpy(&ctx->line[ctx->ofs], str, len * sizeof(WCHAR)); |
| 192 | ctx->len += len; |
| 193 | ctx->line[ctx->len] = 0; |
Eric Pouech | c19bb1a | 2001-12-21 20:29:10 +0000 | [diff] [blame] | 194 | SetConsoleCursorPosition(ctx->hConOut, WCEL_GetCoord(ctx, ctx->ofs)); |
| 195 | WriteConsoleW(ctx->hConOut, &ctx->line[ctx->ofs], ctx->len - ctx->ofs, NULL, NULL); |
Eric Pouech | 0b83d4c | 2001-11-23 23:04:58 +0000 | [diff] [blame] | 196 | ctx->ofs += len; |
| 197 | } |
| 198 | |
| 199 | static void WCEL_InsertChar(WCEL_Context* ctx, WCHAR c) |
| 200 | { |
| 201 | WCHAR buffer[2]; |
| 202 | |
| 203 | /* do not insert 0..31 control characters */ |
| 204 | if (c < ' ') |
| 205 | { |
| 206 | if (c != '\t') return; |
| 207 | } |
| 208 | buffer[0] = c; |
| 209 | buffer[1] = 0; |
| 210 | WCEL_InsertString(ctx, buffer); |
| 211 | } |
| 212 | |
| 213 | static void WCEL_SaveYank(WCEL_Context* ctx, int beg, int end) |
| 214 | { |
| 215 | int len = end - beg; |
| 216 | ctx->yanked = HeapReAlloc(GetProcessHeap(), 0, ctx->yanked, (len + 1) * sizeof(WCHAR)); |
| 217 | if (!ctx->yanked) return; |
| 218 | memcpy(ctx->yanked, &ctx->line[beg], len * sizeof(WCHAR)); |
| 219 | ctx->yanked[len] = 0; |
| 220 | } |
| 221 | |
| 222 | /* FIXME NTDLL doesn't export iswalnum, and I don't want to link in msvcrt when most |
| 223 | * of the data lay in unicode lib |
| 224 | */ |
| 225 | static inline BOOL WCEL_iswalnum(WCHAR wc) |
| 226 | { |
| 227 | return get_char_typeW(wc) & (C1_ALPHA|C1_DIGIT|C1_LOWER|C1_UPPER); |
| 228 | } |
| 229 | |
| 230 | static int WCEL_GetLeftWordTransition(WCEL_Context* ctx, int ofs) |
| 231 | { |
| 232 | ofs--; |
| 233 | while (ofs >= 0 && !WCEL_iswalnum(ctx->line[ofs])) ofs--; |
| 234 | while (ofs >= 0 && WCEL_iswalnum(ctx->line[ofs])) ofs--; |
| 235 | if (ofs >= 0) ofs++; |
| 236 | return max(ofs, 0); |
| 237 | } |
| 238 | |
| 239 | static int WCEL_GetRightWordTransition(WCEL_Context* ctx, int ofs) |
| 240 | { |
| 241 | ofs++; |
| 242 | while (ofs <= ctx->len && !WCEL_iswalnum(ctx->line[ofs])) ofs++; |
| 243 | while (ofs <= ctx->len && WCEL_iswalnum(ctx->line[ofs])) ofs++; |
| 244 | return min(ofs, ctx->len); |
| 245 | } |
| 246 | |
| 247 | static WCHAR* WCEL_GetHistory(WCEL_Context* ctx, int idx) |
| 248 | { |
| 249 | WCHAR* ptr; |
| 250 | |
| 251 | if (idx == ctx->histSize - 1) |
| 252 | { |
| 253 | ptr = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(ctx->histCurr) + 1) * sizeof(WCHAR)); |
| 254 | lstrcpyW(ptr, ctx->histCurr); |
| 255 | } |
| 256 | else |
| 257 | { |
| 258 | int len = CONSOLE_GetHistory(idx, NULL, 0); |
| 259 | |
| 260 | if ((ptr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)))) |
| 261 | { |
| 262 | CONSOLE_GetHistory(idx, ptr, len); |
| 263 | } |
| 264 | } |
| 265 | return ptr; |
| 266 | } |
| 267 | |
| 268 | static void WCEL_HistoryInit(WCEL_Context* ctx) |
| 269 | { |
| 270 | ctx->histPos = CONSOLE_GetNumHistoryEntries(); |
| 271 | ctx->histSize = ctx->histPos + 1; |
| 272 | ctx->histCurr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR)); |
| 273 | } |
| 274 | |
| 275 | static void WCEL_MoveToHist(WCEL_Context* ctx, int idx) |
| 276 | { |
| 277 | WCHAR* data = WCEL_GetHistory(ctx, idx); |
| 278 | int len = lstrlenW(data) + 1; |
| 279 | |
| 280 | /* save current line edition for recall when needed (FIXME seems broken to me) */ |
| 281 | if (ctx->histPos == ctx->histSize - 1) |
| 282 | { |
| 283 | if (ctx->histCurr) HeapFree(GetProcessHeap(), 0, ctx->histCurr); |
| 284 | ctx->histCurr = HeapAlloc(GetProcessHeap(), 0, (ctx->len + 1) * sizeof(WCHAR)); |
| 285 | memcpy(ctx->histCurr, ctx->line, (ctx->len + 1) * sizeof(WCHAR)); |
| 286 | } |
| 287 | /* need to clean also the screen if new string is shorter than old one */ |
| 288 | WCEL_DeleteString(ctx, 0, ctx->len); |
| 289 | ctx->ofs = 0; |
| 290 | /* insert new string */ |
| 291 | if (WCEL_Grow(ctx, len)) |
| 292 | { |
| 293 | WCEL_InsertString(ctx, data); |
| 294 | HeapFree(GetProcessHeap(), 0, data); |
| 295 | ctx->histPos = idx; |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | /* ==================================================================== |
| 300 | * |
| 301 | * basic edition functions |
| 302 | * |
| 303 | * ====================================================================*/ |
| 304 | |
| 305 | static void WCEL_Done(WCEL_Context* ctx) |
| 306 | { |
| 307 | if (!WCEL_Grow(ctx, 1)) return; |
| 308 | ctx->line[ctx->len++] = '\n'; |
| 309 | ctx->line[ctx->len] = 0; |
| 310 | WriteConsoleA(ctx->hConOut, "\n", 1, NULL, NULL); |
| 311 | ctx->done = 1; |
| 312 | } |
| 313 | |
| 314 | static void WCEL_MoveLeft(WCEL_Context* ctx) |
| 315 | { |
| 316 | if (ctx->ofs > 0) ctx->ofs--; |
| 317 | } |
| 318 | |
| 319 | static void WCEL_MoveRight(WCEL_Context* ctx) |
| 320 | { |
| 321 | if (ctx->ofs < ctx->len) ctx->ofs++; |
| 322 | } |
| 323 | |
| 324 | static void WCEL_MoveToLeftWord(WCEL_Context* ctx) |
| 325 | { |
| 326 | int new_ofs = WCEL_GetLeftWordTransition(ctx, ctx->ofs); |
| 327 | if (new_ofs != ctx->ofs) ctx->ofs = new_ofs; |
| 328 | } |
| 329 | |
| 330 | static void WCEL_MoveToRightWord(WCEL_Context* ctx) |
| 331 | { |
| 332 | int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs); |
| 333 | if (new_ofs != ctx->ofs) ctx->ofs = new_ofs; |
| 334 | } |
| 335 | |
| 336 | static void WCEL_MoveToBeg(WCEL_Context* ctx) |
| 337 | { |
| 338 | ctx->ofs = 0; |
| 339 | } |
| 340 | |
| 341 | static void WCEL_MoveToEnd(WCEL_Context* ctx) |
| 342 | { |
| 343 | ctx->ofs = ctx->len; |
| 344 | } |
| 345 | |
| 346 | static void WCEL_SetMark(WCEL_Context* ctx) |
| 347 | { |
| 348 | ctx->mark = ctx->ofs; |
| 349 | } |
| 350 | |
| 351 | static void WCEL_ExchangeMark(WCEL_Context* ctx) |
| 352 | { |
| 353 | unsigned tmp; |
| 354 | |
| 355 | if (ctx->mark > ctx->len) return; |
| 356 | tmp = ctx->ofs; |
| 357 | ctx->ofs = ctx->mark; |
| 358 | ctx->mark = tmp; |
| 359 | } |
| 360 | |
| 361 | static void WCEL_CopyMarkedZone(WCEL_Context* ctx) |
| 362 | { |
| 363 | unsigned beg, end; |
| 364 | |
| 365 | if (ctx->mark > ctx->len || ctx->mark == ctx->ofs) return; |
| 366 | if (ctx->mark > ctx->ofs) |
| 367 | { |
| 368 | beg = ctx->ofs; end = ctx->mark; |
| 369 | } |
| 370 | else |
| 371 | { |
| 372 | beg = ctx->mark; end = ctx->ofs; |
| 373 | } |
| 374 | WCEL_SaveYank(ctx, beg, end); |
| 375 | } |
| 376 | |
| 377 | static void WCEL_TransposeChar(WCEL_Context* ctx) |
| 378 | { |
| 379 | WCHAR c; |
| 380 | |
| 381 | if (!ctx->ofs || ctx->ofs == ctx->len) return; |
| 382 | |
| 383 | c = ctx->line[ctx->ofs]; |
| 384 | ctx->line[ctx->ofs] = ctx->line[ctx->ofs - 1]; |
| 385 | ctx->line[ctx->ofs - 1] = c; |
| 386 | |
| 387 | WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[ctx->ofs - 1], 2, WCEL_GetCoord(ctx, ctx->ofs - 1), NULL); |
| 388 | ctx->ofs++; |
| 389 | } |
| 390 | |
| 391 | static void WCEL_TransposeWords(WCEL_Context* ctx) |
| 392 | { |
| 393 | FIXME("NIY\n"); |
| 394 | } |
| 395 | |
| 396 | static void WCEL_LowerCaseWord(WCEL_Context* ctx) |
| 397 | { |
| 398 | int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs); |
| 399 | if (new_ofs != ctx->ofs) |
| 400 | { |
| 401 | int i; |
| 402 | for (i = ctx->ofs; i <= new_ofs; i++) |
| 403 | ctx->line[i] = tolowerW(ctx->line[i]); |
| 404 | WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[ctx->ofs], new_ofs - ctx->ofs + 1, |
| 405 | WCEL_GetCoord(ctx, ctx->ofs), NULL); |
| 406 | ctx->ofs = new_ofs; |
| 407 | } |
| 408 | } |
| 409 | |
| 410 | static void WCEL_UpperCaseWord(WCEL_Context* ctx) |
| 411 | { |
| 412 | int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs); |
| 413 | if (new_ofs != ctx->ofs) |
| 414 | { |
| 415 | int i; |
| 416 | for (i = ctx->ofs; i <= new_ofs; i++) |
| 417 | ctx->line[i] = toupperW(ctx->line[i]); |
| 418 | WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[ctx->ofs], new_ofs - ctx->ofs + 1, |
| 419 | WCEL_GetCoord(ctx, ctx->ofs), NULL); |
| 420 | ctx->ofs = new_ofs; |
| 421 | } |
| 422 | } |
| 423 | |
| 424 | static void WCEL_CapitalizeWord(WCEL_Context* ctx) |
| 425 | { |
| 426 | int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs); |
| 427 | if (new_ofs != ctx->ofs) |
| 428 | { |
| 429 | int i; |
| 430 | |
| 431 | ctx->line[ctx->ofs] = toupperW(ctx->line[ctx->ofs]); |
| 432 | for (i = ctx->ofs + 1; i <= new_ofs; i++) |
| 433 | ctx->line[i] = tolowerW(ctx->line[i]); |
| 434 | WriteConsoleOutputCharacterW(ctx->hConOut, &ctx->line[ctx->ofs], new_ofs - ctx->ofs + 1, |
| 435 | WCEL_GetCoord(ctx, ctx->ofs), NULL); |
| 436 | ctx->ofs = new_ofs; |
| 437 | } |
| 438 | } |
| 439 | |
| 440 | static void WCEL_Yank(WCEL_Context* ctx) |
| 441 | { |
| 442 | WCEL_InsertString(ctx, ctx->yanked); |
| 443 | HeapFree(GetProcessHeap(), 0, ctx->yanked); |
| 444 | ctx->yanked = NULL; |
| 445 | } |
| 446 | |
| 447 | static void WCEL_KillToEndOfLine(WCEL_Context* ctx) |
| 448 | { |
| 449 | WCEL_SaveYank(ctx, ctx->ofs, ctx->len); |
| 450 | WCEL_DeleteString(ctx, ctx->ofs, ctx->len); |
| 451 | } |
| 452 | |
| 453 | static void WCEL_KillMarkedZone(WCEL_Context* ctx) |
| 454 | { |
| 455 | unsigned beg, end; |
| 456 | |
| 457 | if (ctx->mark > ctx->len || ctx->mark == ctx->ofs) return; |
| 458 | if (ctx->mark > ctx->ofs) |
| 459 | { |
| 460 | beg = ctx->ofs; end = ctx->mark; |
| 461 | } |
| 462 | else |
| 463 | { |
| 464 | beg = ctx->mark; end = ctx->ofs; |
| 465 | } |
| 466 | WCEL_SaveYank(ctx, beg, end); |
| 467 | WCEL_DeleteString(ctx, beg, end); |
| 468 | ctx->ofs = beg; |
| 469 | } |
| 470 | |
| 471 | static void WCEL_DeletePrevChar(WCEL_Context* ctx) |
| 472 | { |
| 473 | if (ctx->ofs) |
| 474 | { |
| 475 | WCEL_DeleteString(ctx, ctx->ofs - 1, ctx->ofs); |
| 476 | ctx->ofs--; |
| 477 | } |
| 478 | } |
| 479 | |
| 480 | static void WCEL_DeleteCurrChar(WCEL_Context* ctx) |
| 481 | { |
| 482 | if (ctx->ofs < ctx->len) |
| 483 | WCEL_DeleteString(ctx, ctx->ofs, ctx->ofs + 1); |
| 484 | } |
| 485 | |
| 486 | static void WCEL_DeleteLeftWord(WCEL_Context* ctx) |
| 487 | { |
| 488 | int new_ofs = WCEL_GetLeftWordTransition(ctx, ctx->ofs); |
| 489 | if (new_ofs != ctx->ofs) |
| 490 | { |
| 491 | WCEL_DeleteString(ctx, new_ofs, ctx->ofs); |
| 492 | ctx->ofs = new_ofs; |
| 493 | } |
| 494 | } |
| 495 | |
| 496 | static void WCEL_DeleteRightWord(WCEL_Context* ctx) |
| 497 | { |
| 498 | int new_ofs = WCEL_GetRightWordTransition(ctx, ctx->ofs); |
| 499 | if (new_ofs != ctx->ofs) |
| 500 | { |
| 501 | WCEL_DeleteString(ctx, ctx->ofs, new_ofs); |
| 502 | } |
| 503 | } |
| 504 | |
| 505 | static void WCEL_MoveToPrevHist(WCEL_Context* ctx) |
| 506 | { |
| 507 | if (ctx->histPos) WCEL_MoveToHist(ctx, ctx->histPos - 1); |
| 508 | } |
| 509 | |
| 510 | static void WCEL_MoveToNextHist(WCEL_Context* ctx) |
| 511 | { |
| 512 | if (ctx->histPos < ctx->histSize - 1) WCEL_MoveToHist(ctx, ctx->histPos + 1); |
| 513 | } |
| 514 | |
| 515 | static void WCEL_MoveToFirstHist(WCEL_Context* ctx) |
| 516 | { |
| 517 | if (ctx->histPos != 0) WCEL_MoveToHist(ctx, 0); |
| 518 | } |
| 519 | |
| 520 | static void WCEL_MoveToLastHist(WCEL_Context* ctx) |
| 521 | { |
| 522 | if (ctx->histPos != ctx->histSize - 1) WCEL_MoveToHist(ctx, ctx->histSize - 1); |
| 523 | } |
| 524 | |
| 525 | /* ==================================================================== |
| 526 | * |
| 527 | * Key Maps |
| 528 | * |
| 529 | * ====================================================================*/ |
| 530 | |
| 531 | #define CTRL(x) ((x) - '@') |
| 532 | static KeyEntry StdKeyMap[] = |
| 533 | { |
| 534 | {/*BACK*/0x08, WCEL_DeletePrevChar }, |
| 535 | {/*RETURN*/0x0d, WCEL_Done }, |
| 536 | {/*DEL*/127, WCEL_DeleteCurrChar }, |
| 537 | { 0, NULL } |
| 538 | }; |
| 539 | |
| 540 | static KeyEntry EmacsKeyMapCtrl[] = |
| 541 | { |
| 542 | { CTRL('@'), WCEL_SetMark }, |
| 543 | { CTRL('A'), WCEL_MoveToBeg }, |
| 544 | { CTRL('B'), WCEL_MoveLeft }, |
| 545 | /* C */ |
| 546 | { CTRL('D'), WCEL_DeleteCurrChar }, |
| 547 | { CTRL('E'), WCEL_MoveToEnd }, |
| 548 | { CTRL('F'), WCEL_MoveRight }, |
| 549 | { CTRL('G'), WCEL_Beep }, |
| 550 | { CTRL('H'), WCEL_DeletePrevChar }, |
| 551 | /* I: meaningless (or tab ???) */ |
| 552 | { CTRL('J'), WCEL_Done }, |
| 553 | { CTRL('K'), WCEL_KillToEndOfLine }, |
| 554 | /* L: [NIY] redraw the whole stuff */ |
| 555 | { CTRL('M'), WCEL_Done }, |
| 556 | { CTRL('N'), WCEL_MoveToNextHist }, |
| 557 | /* O; insert line... meaningless */ |
| 558 | { CTRL('P'), WCEL_MoveToPrevHist }, |
| 559 | /* Q: [NIY] quoting... */ |
| 560 | /* R: [NIY] search backwards... */ |
| 561 | /* S: [NIY] search forwards... */ |
| 562 | { CTRL('T'), WCEL_TransposeChar }, |
| 563 | /* U: [NIY] set repeat count... */ |
| 564 | /* V: paragraph down... meaningless */ |
| 565 | { CTRL('W'), WCEL_KillMarkedZone }, |
| 566 | { CTRL('X'), WCEL_ExchangeMark }, |
| 567 | { CTRL('Y'), WCEL_Yank }, |
| 568 | /* Z: meaningless */ |
| 569 | { 0, NULL } |
| 570 | }; |
| 571 | |
| 572 | static KeyEntry EmacsKeyMapAlt[] = |
| 573 | { |
| 574 | {/*DEL*/127, WCEL_DeleteLeftWord }, |
| 575 | { '<', WCEL_MoveToFirstHist }, |
| 576 | { '>', WCEL_MoveToLastHist }, |
| 577 | { '?', WCEL_Beep }, |
| 578 | { 'b', WCEL_MoveToLeftWord }, |
| 579 | { 'c', WCEL_CapitalizeWord }, |
| 580 | { 'd', WCEL_DeleteRightWord }, |
| 581 | { 'f', WCEL_MoveToRightWord }, |
| 582 | { 'l', WCEL_LowerCaseWord }, |
| 583 | { 't', WCEL_TransposeWords }, |
| 584 | { 'u', WCEL_UpperCaseWord }, |
| 585 | { 'w', WCEL_CopyMarkedZone }, |
| 586 | { 0, NULL } |
| 587 | }; |
| 588 | |
| 589 | static KeyEntry EmacsKeyMapExtended[] = |
| 590 | { |
Dmitry Timoshkov | dac9dfc | 2002-02-12 18:45:46 +0000 | [diff] [blame] | 591 | {/*RETURN*/ 0x0d, WCEL_Done }, |
Eric Pouech | 0b83d4c | 2001-11-23 23:04:58 +0000 | [diff] [blame] | 592 | {/*VK_PRIOR*/0x21, WCEL_MoveToPrevHist }, |
| 593 | {/*VK_NEXT*/0x22, WCEL_MoveToNextHist }, |
Dmitry Timoshkov | dac9dfc | 2002-02-12 18:45:46 +0000 | [diff] [blame] | 594 | {/*VK_END*/ 0x23, WCEL_MoveToEnd }, |
| 595 | {/*VK_HOME*/ 0x24, WCEL_MoveToBeg }, |
Eric Pouech | 0b83d4c | 2001-11-23 23:04:58 +0000 | [diff] [blame] | 596 | {/*VK_RIGHT*/0x27, WCEL_MoveRight }, |
| 597 | {/*VK_LEFT*/0x25, WCEL_MoveLeft }, |
| 598 | { 0, NULL } |
| 599 | }; |
| 600 | |
| 601 | static KeyMap EmacsKeyMap[] = |
| 602 | { |
| 603 | {0x00000000, 1, StdKeyMap}, |
| 604 | {0x00000001, 1, EmacsKeyMapAlt}, /* left alt */ |
| 605 | {0x00000002, 1, EmacsKeyMapAlt}, /* right alt */ |
| 606 | {0x00000004, 1, EmacsKeyMapCtrl}, /* left ctrl */ |
| 607 | {0x00000008, 1, EmacsKeyMapCtrl}, /* right ctrl */ |
| 608 | {0x00000100, 0, EmacsKeyMapExtended}, |
| 609 | {0, 0, 0} |
| 610 | }; |
| 611 | |
| 612 | static KeyEntry Win32KeyMapExtended[] = |
| 613 | { |
| 614 | {/*VK_LEFT*/ 0x25, WCEL_MoveLeft }, |
| 615 | {/*VK_RIGHT*/0x27, WCEL_MoveRight }, |
| 616 | {/*VK_HOME*/ 0x24, WCEL_MoveToBeg }, |
| 617 | {/*VK_END*/ 0x23, WCEL_MoveToEnd }, |
| 618 | {/*VK_UP*/ 0x26, WCEL_MoveToPrevHist }, |
| 619 | {/*VK_DOWN*/ 0x28, WCEL_MoveToNextHist }, |
| 620 | { 0, NULL } |
| 621 | }; |
| 622 | |
| 623 | static KeyEntry Win32KeyMapCtrlExtended[] = |
| 624 | { |
| 625 | {/*VK_LEFT*/ 0x25, WCEL_MoveToLeftWord }, |
| 626 | {/*VK_RIGHT*/0x27, WCEL_MoveToRightWord }, |
| 627 | { 0, NULL } |
| 628 | }; |
| 629 | |
| 630 | KeyMap Win32KeyMap[] = |
| 631 | { |
| 632 | {0x00000000, 1, StdKeyMap}, |
| 633 | {0x00000100, 0, Win32KeyMapExtended}, |
| 634 | {0x00000104, 0, Win32KeyMapCtrlExtended}, |
| 635 | {0x00000108, 0, Win32KeyMapCtrlExtended}, |
| 636 | {0, 0, 0} |
| 637 | }; |
| 638 | #undef CTRL |
| 639 | |
| 640 | /* ==================================================================== |
| 641 | * |
| 642 | * Read line master function |
| 643 | * |
| 644 | * ====================================================================*/ |
| 645 | |
| 646 | WCHAR* CONSOLE_Readline(HANDLE hConsoleIn, int use_emacs) |
| 647 | { |
| 648 | WCEL_Context ctx; |
| 649 | INPUT_RECORD ir; |
| 650 | KeyMap* km; |
| 651 | KeyEntry* ke; |
| 652 | unsigned ofs; |
| 653 | void (*func)(struct WCEL_Context* ctx); |
Eric Pouech | 824a7f1 | 2002-01-10 18:20:51 +0000 | [diff] [blame] | 654 | DWORD ks; |
Eric Pouech | 0b83d4c | 2001-11-23 23:04:58 +0000 | [diff] [blame] | 655 | |
| 656 | memset(&ctx, 0, sizeof(ctx)); |
| 657 | ctx.hConIn = hConsoleIn; |
| 658 | WCEL_HistoryInit(&ctx); |
| 659 | if ((ctx.hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, |
| 660 | OPEN_EXISTING, 0, 0 )) == INVALID_HANDLE_VALUE || |
| 661 | !GetConsoleScreenBufferInfo(ctx.hConOut, &ctx.csbi)) |
| 662 | return NULL; |
| 663 | if (!WCEL_Grow(&ctx, 1)) |
| 664 | { |
| 665 | CloseHandle(ctx.hConOut); |
| 666 | return NULL; |
| 667 | } |
| 668 | ctx.line[0] = 0; |
| 669 | |
| 670 | /* EPP WCEL_Dump(&ctx, "init"); */ |
| 671 | |
| 672 | while (!ctx.done && !ctx.error && WCEL_Get(&ctx, &ir)) |
| 673 | { |
| 674 | if (ir.EventType != KEY_EVENT || !ir.Event.KeyEvent.bKeyDown) continue; |
| 675 | TRACE("key%s repeatCount=%u, keyCode=%02x scanCode=%02x char=%02x keyState=%08lx\n", |
| 676 | ir.Event.KeyEvent.bKeyDown ? "Down" : "Up ", ir.Event.KeyEvent.wRepeatCount, |
| 677 | ir.Event.KeyEvent.wVirtualKeyCode, ir.Event.KeyEvent.wVirtualScanCode, |
| 678 | ir.Event.KeyEvent.uChar.UnicodeChar, ir.Event.KeyEvent.dwControlKeyState); |
| 679 | |
| 680 | /* EPP WCEL_Dump(&ctx, "before func"); */ |
| 681 | ofs = ctx.ofs; |
Eric Pouech | 824a7f1 | 2002-01-10 18:20:51 +0000 | [diff] [blame] | 682 | /* mask out some bits which don't interest us */ |
| 683 | ks = ir.Event.KeyEvent.dwControlKeyState & ~(NUMLOCK_ON|SCROLLLOCK_ON|CAPSLOCK_ON); |
Eric Pouech | 0b83d4c | 2001-11-23 23:04:58 +0000 | [diff] [blame] | 684 | |
| 685 | func = NULL; |
| 686 | for (km = (use_emacs) ? EmacsKeyMap : Win32KeyMap; km->entries != NULL; km++) |
| 687 | { |
Eric Pouech | 824a7f1 | 2002-01-10 18:20:51 +0000 | [diff] [blame] | 688 | if (km->keyState != ks) |
Eric Pouech | 0b83d4c | 2001-11-23 23:04:58 +0000 | [diff] [blame] | 689 | continue; |
| 690 | if (km->chkChar) |
| 691 | { |
| 692 | for (ke = &km->entries[0]; ke->func != 0; ke++) |
| 693 | if (ke->val == ir.Event.KeyEvent.uChar.UnicodeChar) break; |
| 694 | } |
| 695 | else |
| 696 | { |
| 697 | for (ke = &km->entries[0]; ke->func != 0; ke++) |
| 698 | if (ke->val == ir.Event.KeyEvent.wVirtualKeyCode) break; |
| 699 | |
| 700 | } |
| 701 | if (ke->func) |
| 702 | { |
| 703 | func = ke->func; |
| 704 | break; |
| 705 | } |
| 706 | } |
| 707 | |
| 708 | if (func) |
| 709 | (func)(&ctx); |
| 710 | else if (!(ir.Event.KeyEvent.dwControlKeyState & (ENHANCED_KEY|LEFT_ALT_PRESSED))) |
| 711 | WCEL_InsertChar(&ctx, ir.Event.KeyEvent.uChar.UnicodeChar); |
| 712 | else TRACE("Dropped event\n"); |
| 713 | |
| 714 | /* EPP WCEL_Dump(&ctx, "after func"); */ |
| 715 | if (ctx.ofs != ofs) |
| 716 | SetConsoleCursorPosition(ctx.hConOut, WCEL_GetCoord(&ctx, ctx.ofs)); |
| 717 | } |
| 718 | if (ctx.error) |
| 719 | { |
| 720 | HeapFree(GetProcessHeap(), 0, ctx.line); |
| 721 | ctx.line = NULL; |
| 722 | } |
| 723 | if (ctx.line) |
| 724 | CONSOLE_AppendHistory(ctx.line); |
| 725 | |
| 726 | CloseHandle(ctx.hConOut); |
| 727 | if (ctx.histCurr) HeapFree(GetProcessHeap(), 0, ctx.histCurr); |
| 728 | return ctx.line; |
| 729 | } |
| 730 | |