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