term.c 18 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Interface to terminfo and termcap libraries
 *
 * Copyright 2010 Eric Pouech
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"
#include "wine/port.h"

24
#include <stdarg.h>
25
#include <stdlib.h>
26 27 28 29 30 31 32 33 34
#ifdef HAVE_NCURSES_H
# include <ncurses.h>
#elif defined(HAVE_CURSES_H)
# include <curses.h>
#endif
/* avoid redefinition warnings */
#undef KEY_EVENT
#undef MOUSE_MOVED

35
#if defined(HAVE_CURSES_H) || defined(HAVE_NCURSES_H)
36
#include <term.h>
37
#endif
38 39 40 41 42 43 44 45 46 47 48

#include <windef.h>
#include <winbase.h>
#include <winnls.h>
#include <wincon.h>
#include "console_private.h"
#include "wine/library.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(console);

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
static const int vkkeyscan_table[256] =
{
     0,0,0,0,0,0,0,0,8,9,0,0,0,13,0,0,0,0,0,19,145,556,0,0,0,0,0,27,0,0,0,
     0,32,305,478,307,308,309,311,222,313,304,312,443,188,189,190,191,48,
     49,50,51,52,53,54,55,56,57,442,186,444,187,446,447,306,321,322,323,
     324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,
     341,342,343,344,345,346,219,220,221,310,445,192,65,66,67,68,69,70,71,
     72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,475,476,477,
     448,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400,0,0,0,0,0,0
};

static const int mapvkey_0[256] =
{
     0,0,0,0,0,0,0,0,14,15,0,0,0,28,0,0,42,29,56,69,58,0,0,0,0,0,0,1,0,0,
     0,0,57,73,81,79,71,75,72,77,80,0,0,0,55,82,83,0,11,2,3,4,5,6,7,8,9,
     10,0,0,0,0,0,0,0,30,48,46,32,18,33,34,35,23,36,37,38,50,49,24,25,16,
     19,31,20,22,47,17,45,21,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,55,78,0,74,
     0,53,59,60,61,62,63,64,65,66,67,68,87,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,69,70,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,39,13,51,12,52,53,41,0,0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,43,27,40,76,96,0,0,0,0,0,0,0,0,
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};

static inline void init_complex_char(INPUT_RECORD* ir, BOOL down, WORD vk, WORD kc, DWORD cks)
{
    ir->EventType			 = KEY_EVENT;
    ir->Event.KeyEvent.bKeyDown	         = down;
    ir->Event.KeyEvent.wRepeatCount	 = 1;
    ir->Event.KeyEvent.wVirtualScanCode  = vk;
    ir->Event.KeyEvent.wVirtualKeyCode   = kc;
    ir->Event.KeyEvent.dwControlKeyState = cks;
    ir->Event.KeyEvent.uChar.UnicodeChar = 0;
}

/******************************************************************
 *		TERM_FillSimpleChar
 *
 */
91
unsigned TERM_FillSimpleChar(WCHAR real_inchar, INPUT_RECORD* ir)
92 93
{
    unsigned            vk;
94
    WCHAR               inchar;
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
    unsigned            numEvent = 0;
    DWORD               cks = 0;

    switch (real_inchar)
    {
    case   9: inchar = real_inchar;
        real_inchar = 27; /* so that we don't think key is ctrl- something */
        break;
    case  13:
    case  10: inchar = '\r';
        real_inchar = 27; /* Fixme: so that we don't think key is ctrl- something */
        break;
    case 127: inchar = '\b';
        break;
    default:
        inchar = real_inchar;
        break;
    }
113
    vk = (inchar < 256) ? vkkeyscan_table[inchar] : 0;
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
    if (vk & 0x0100)
        init_complex_char(&ir[numEvent++], 1, 0x2a, 0x10, SHIFT_PRESSED);
    if ((vk & 0x0200) || (unsigned char)real_inchar <= 26)
        init_complex_char(&ir[numEvent++], 1, 0x1d, 0x11, LEFT_CTRL_PRESSED);
    if (vk & 0x0400)
        init_complex_char(&ir[numEvent++], 1, 0x38, 0x12, LEFT_ALT_PRESSED);

    ir[numEvent].EventType                        = KEY_EVENT;
    ir[numEvent].Event.KeyEvent.bKeyDown          = 1;
    ir[numEvent].Event.KeyEvent.wRepeatCount      = 1;
    ir[numEvent].Event.KeyEvent.dwControlKeyState = cks;
    if (vk & 0x0100)
        ir[numEvent].Event.KeyEvent.dwControlKeyState |= SHIFT_PRESSED;
    if ((vk & 0x0200) || (unsigned char)real_inchar <= 26)
        ir[numEvent].Event.KeyEvent.dwControlKeyState |= LEFT_CTRL_PRESSED;
    if (vk & 0x0400)
        ir[numEvent].Event.KeyEvent.dwControlKeyState |= LEFT_ALT_PRESSED;
    ir[numEvent].Event.KeyEvent.wVirtualKeyCode = vk;
    ir[numEvent].Event.KeyEvent.wVirtualScanCode = mapvkey_0[vk & 0x00ff]; /* VirtualKeyCodes to ScanCode */

134
    ir[numEvent].Event.KeyEvent.uChar.UnicodeChar = inchar;
135 136 137 138 139 140 141 142 143 144 145 146 147 148
    ir[numEvent + 1] = ir[numEvent];
    ir[numEvent + 1].Event.KeyEvent.bKeyDown = 0;

    numEvent += 2;

    if (vk & 0x0400)
        init_complex_char(&ir[numEvent++], 0, 0x38, 0x12, LEFT_ALT_PRESSED);
    if ((vk & 0x0200) || (unsigned char)real_inchar <= 26)
        init_complex_char(&ir[numEvent++], 0, 0x1d, 0x11, 0);
    if (vk & 0x0100)
        init_complex_char(&ir[numEvent++], 0, 0x2a, 0x10, 0);
    return numEvent;
}

149 150 151 152 153 154 155 156 157 158 159 160
#if defined(SONAME_LIBCURSES) || defined(SONAME_LIBNCURSES)

#ifdef HAVE_NCURSES_H
# define CURSES_NAME "ncurses"
#else
# define CURSES_NAME "curses"
#endif

static void *nc_handle = NULL;

#define MAKE_FUNCPTR(f) static typeof(f) * p_##f;

161
MAKE_FUNCPTR(putp)
162
MAKE_FUNCPTR(setupterm)
163 164
MAKE_FUNCPTR(tigetstr)
MAKE_FUNCPTR(tparm)
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179

#undef MAKE_FUNCPTR

/**********************************************************************/

static BOOL TERM_bind_libcurses(void)
{
#ifdef SONAME_LIBNCURSES
    static const char ncname[] = SONAME_LIBNCURSES;
#else
    static const char ncname[] = SONAME_LIBCURSES;
#endif

    if (!(nc_handle = wine_dlopen(ncname, RTLD_NOW, NULL, 0)))
    {
180
        MESSAGE("Wine cannot find the " CURSES_NAME " library (%s).\n", ncname);
181 182 183 184 185 186
        return FALSE;
    }

#define LOAD_FUNCPTR(f)                                      \
    if((p_##f = wine_dlsym(nc_handle, #f, NULL, 0)) == NULL) \
    {                                                        \
187
        WARN("Can't find symbol %s\n", #f);                  \
188 189 190
        goto sym_not_found;                                  \
    }

191
    LOAD_FUNCPTR(putp)
192
    LOAD_FUNCPTR(setupterm)
193 194
    LOAD_FUNCPTR(tigetstr)
    LOAD_FUNCPTR(tparm)
195 196 197 198 199 200

#undef LOAD_FUNCPTR

    return TRUE;

sym_not_found:
201
    MESSAGE(
202 203 204 205 206 207 208 209
      "Wine cannot find certain functions that it needs inside the "
       CURSES_NAME "\nlibrary.  To enable Wine to use " CURSES_NAME
      " please upgrade your " CURSES_NAME "\nlibraries\n");
    wine_dlclose(nc_handle, NULL, 0);
    nc_handle = NULL;
    return FALSE;
}

210
#define putp      p_putp
211
#define setupterm p_setupterm
212 213 214 215 216 217 218 219 220 221 222
#define tigetstr  p_tigetstr
#define tparm     p_tparm

struct dbkey_descr
{
    enum dbkey_kind {dbk_simple, dbk_complex} kind;
    DWORD_PTR   p1;
    DWORD_PTR   p2;
    DWORD_PTR   p3;
};

223
struct dbkey_init
224
{
225 226
    const char*         string_normal;
    const char*         string_xterm;
227 228 229
    struct dbkey_descr  descr;
};

230
static const struct dbkey_init TERM_dbkey_init[] = {
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
    {"kcud1", "kDN",  {dbk_complex, 0x50, 0x28, 0}},
    {"kcuu1", "kUP",  {dbk_complex, 0x48, 0x26, 0}},
    {"kcub1", "kLFT", {dbk_complex, 0x4b, 0x25, 0}},
    {"kcuf1", "kRIT", {dbk_complex, 0x4d, 0x27, 0}},
    {"khome", "kHOM", {dbk_complex, 0x47, 0x24, 0}},
    {"kbs",   NULL,   {dbk_simple,  0x7f, 0x00, 0}},
    {"kf1",   NULL,   {dbk_complex, 0x3b, 0x70, 0}},
    {"kf2",   NULL,   {dbk_complex, 0x3c, 0x71, 0}},
    {"kf3",   NULL,   {dbk_complex, 0x3d, 0x72, 0}},
    {"kf4",   NULL,   {dbk_complex, 0x3e, 0x73, 0}},
    {"kf5",   NULL,   {dbk_complex, 0x3f, 0x74, 0}},
    {"kf6",   NULL,   {dbk_complex, 0x40, 0x75, 0}},
    {"kf7",   NULL,   {dbk_complex, 0x41, 0x76, 0}},
    {"kf8",   NULL,   {dbk_complex, 0x42, 0x77, 0}},
    {"kf9",   NULL,   {dbk_complex, 0x43, 0x78, 0}},
    {"kf10",  NULL,   {dbk_complex, 0x44, 0x79, 0}},
    {"kf11",  NULL,   {dbk_complex, 0xd9, 0x7a, 0}},
    {"kf12",  NULL,   {dbk_complex, 0xda, 0x7b, 0}},
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
    {"kf13",  NULL,   {dbk_complex, 0x3b, 0x70, SHIFT_PRESSED}},
    {"kf14",  NULL,   {dbk_complex, 0x3c, 0x71, SHIFT_PRESSED}},
    {"kf15",  NULL,   {dbk_complex, 0x3d, 0x72, SHIFT_PRESSED}},
    {"kf16",  NULL,   {dbk_complex, 0x3e, 0x73, SHIFT_PRESSED}},
    {"kf17",  NULL,   {dbk_complex, 0x3f, 0x74, SHIFT_PRESSED}},
    {"kf18",  NULL,   {dbk_complex, 0x40, 0x75, SHIFT_PRESSED}},
    {"kf19",  NULL,   {dbk_complex, 0x41, 0x76, SHIFT_PRESSED}},
    {"kf20",  NULL,   {dbk_complex, 0x42, 0x77, SHIFT_PRESSED}},
    {"kf21",  NULL,   {dbk_complex, 0x43, 0x78, SHIFT_PRESSED}},
    {"kf22",  NULL,   {dbk_complex, 0x44, 0x79, SHIFT_PRESSED}},
    {"kf23",  NULL,   {dbk_complex, 0xd9, 0x7a, SHIFT_PRESSED}},
    {"kf24",  NULL,   {dbk_complex, 0xda, 0x7b, SHIFT_PRESSED}},
    {"kf25",  NULL,   {dbk_complex, 0x3b, 0x70, LEFT_CTRL_PRESSED}},
    {"kf26",  NULL,   {dbk_complex, 0x3c, 0x71, LEFT_CTRL_PRESSED}},
    {"kf27",  NULL,   {dbk_complex, 0x3d, 0x72, LEFT_CTRL_PRESSED}},
    {"kf28",  NULL,   {dbk_complex, 0x3e, 0x73, LEFT_CTRL_PRESSED}},
    {"kf29",  NULL,   {dbk_complex, 0x3f, 0x74, LEFT_CTRL_PRESSED}},
    {"kf30",  NULL,   {dbk_complex, 0x40, 0x75, LEFT_CTRL_PRESSED}},
    {"kf31",  NULL,   {dbk_complex, 0x41, 0x76, LEFT_CTRL_PRESSED}},
    {"kf32",  NULL,   {dbk_complex, 0x42, 0x77, LEFT_CTRL_PRESSED}},
    {"kf33",  NULL,   {dbk_complex, 0x43, 0x78, LEFT_CTRL_PRESSED}},
    {"kf34",  NULL,   {dbk_complex, 0x44, 0x79, LEFT_CTRL_PRESSED}},
    {"kf35",  NULL,   {dbk_complex, 0xd9, 0x7a, LEFT_CTRL_PRESSED}},
    {"kf36",  NULL,   {dbk_complex, 0xda, 0x7b, LEFT_CTRL_PRESSED}},
    {"kf37",  NULL,   {dbk_complex, 0x3b, 0x70, LEFT_CTRL_PRESSED|SHIFT_PRESSED}},
    {"kf38",  NULL,   {dbk_complex, 0x3c, 0x71, LEFT_CTRL_PRESSED|SHIFT_PRESSED}},
    {"kf39",  NULL,   {dbk_complex, 0x3d, 0x72, LEFT_CTRL_PRESSED|SHIFT_PRESSED}},
    {"kf40",  NULL,   {dbk_complex, 0x3e, 0x73, LEFT_CTRL_PRESSED|SHIFT_PRESSED}},
    {"kf41",  NULL,   {dbk_complex, 0x3f, 0x74, LEFT_CTRL_PRESSED|SHIFT_PRESSED}},
    {"kf42",  NULL,   {dbk_complex, 0x40, 0x75, LEFT_CTRL_PRESSED|SHIFT_PRESSED}},
    {"kf43",  NULL,   {dbk_complex, 0x41, 0x76, LEFT_CTRL_PRESSED|SHIFT_PRESSED}},
    {"kf44",  NULL,   {dbk_complex, 0x42, 0x77, LEFT_CTRL_PRESSED|SHIFT_PRESSED}},
    {"kf45",  NULL,   {dbk_complex, 0x43, 0x78, LEFT_CTRL_PRESSED|SHIFT_PRESSED}},
    {"kf46",  NULL,   {dbk_complex, 0x44, 0x79, LEFT_CTRL_PRESSED|SHIFT_PRESSED}},
    {"kf47",  NULL,   {dbk_complex, 0xd9, 0x7a, LEFT_CTRL_PRESSED|SHIFT_PRESSED}},
    {"kf48",  NULL,   {dbk_complex, 0xda, 0x7b, LEFT_CTRL_PRESSED|SHIFT_PRESSED}},
    {"kf49",  NULL,   {dbk_complex, 0x3b, 0x70, LEFT_ALT_PRESSED}},
    {"kf50",  NULL,   {dbk_complex, 0x3c, 0x71, LEFT_ALT_PRESSED}},
    {"kf51",  NULL,   {dbk_complex, 0x3d, 0x72, LEFT_ALT_PRESSED}},
    {"kf52",  NULL,   {dbk_complex, 0x3e, 0x73, LEFT_ALT_PRESSED}},
    {"kf53",  NULL,   {dbk_complex, 0x3f, 0x74, LEFT_ALT_PRESSED}},
    {"kf54",  NULL,   {dbk_complex, 0x40, 0x75, LEFT_ALT_PRESSED}},
    {"kf55",  NULL,   {dbk_complex, 0x41, 0x76, LEFT_ALT_PRESSED}},
    {"kf56",  NULL,   {dbk_complex, 0x42, 0x77, LEFT_ALT_PRESSED}},
    {"kf57",  NULL,   {dbk_complex, 0x43, 0x78, LEFT_ALT_PRESSED}},
    {"kf58",  NULL,   {dbk_complex, 0x44, 0x79, LEFT_ALT_PRESSED}},
    {"kf59",  NULL,   {dbk_complex, 0xd9, 0x7a, LEFT_ALT_PRESSED}},
    {"kf60",  NULL,   {dbk_complex, 0xda, 0x7b, LEFT_ALT_PRESSED}},
    {"kf61",  NULL,   {dbk_complex, 0x3b, 0x70, LEFT_ALT_PRESSED|SHIFT_PRESSED}},
    {"kf62",  NULL,   {dbk_complex, 0x3c, 0x71, LEFT_ALT_PRESSED|SHIFT_PRESSED}},
    {"kf63",  NULL,   {dbk_complex, 0x3d, 0x72, LEFT_ALT_PRESSED|SHIFT_PRESSED}},

301 302 303 304 305 306
    {"kdch1", "kDC",  {dbk_complex, 0x53, 0x2e, 0}},
    {"kich1", "kIC",  {dbk_complex, 0x52, 0x2d, 0}},
    {"knp",   "kNXT", {dbk_complex, 0x51, 0x22, 0}},
    {"kpp",   "kPRV", {dbk_complex, 0x49, 0x21, 0}},
    {"kcbt",  NULL,   {dbk_simple,  0x09, 0x00, SHIFT_PRESSED}},
    {"kend",  "kEND", {dbk_complex, 0x4f, 0x23, 0}},
307

308
    /* {"kmous", NULL, }, */
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
    /* Still some keys to manage:
       KEY_DL           KEY_IL          KEY_EIC         KEY_CLEAR               KEY_EOS
       KEY_EOL          KEY_SF          KEY_SR          KEY_STAB                KEY_CTAB
       KEY_CATAB        KEY_ENTER       KEY_SRESET      KEY_RESET               KEY_PRINT
       KEY_LL           KEY_A1          KEY_A3          KEY_B2                  KEY_C1
       KEY_C3           KEY_BEG         KEY_CANCEL      KEY_CLOSE               KEY_COMMAND
       KEY_COPY         KEY_CREATE      KEY_EXIT        KEY_FIND                KEY_HELP
       KEY_MARK         KEY_MESSAGE     KEY_MOVE        KEY_NEXT                KEY_OPEN
       KEY_OPTIONS      KEY_PREVIOUS    KEY_REDO        KEY_REFERENCE           KEY_REFRESH
       KEY_REPLACE      KEY_RESTART     KEY_RESUME      KEY_SAVE                KEY_SBEG
       KEY_SCANCEL      KEY_SCOMMAND    KEY_SCOPY       KEY_SCREATE             KEY_RESIZE
       KEY_SDL          KEY_SELECT      KEY_SEOL        KEY_SEXIT               KEY_SFIND
       KEY_SHELP        KEY_SMESSAGE    KEY_SMOVE       KEY_SNEXT               KEY_SOPTIONS
       KEY_SPREVIOUS    KEY_SPRINT      KEY_SREDO       KEY_SREPLACE            KEY_SRSUME
       KEY_SSAVE        KEY_SSUSPEND    KEY_SUNDO       KEY_SUSPEND             KEY_UNDO
    */
};

327 328 329 330 331 332 333
struct dbkey_pair
{
    const char*         string;
    unsigned            string_len;
    struct dbkey_descr  descr;
};

334 335 336 337
static struct dbkey_pair*       TERM_dbkey;
static unsigned                 TERM_dbkey_size;
static unsigned                 TERM_dbkey_index;

338 339 340 341 342 343 344
static int  TERM_dbkey_cmp(const void* p1, const void* p2)
{
    const struct dbkey_pair*  kp1 = p1;
    const struct dbkey_pair*  kp2 = p2;
    return strcmp(kp1->string, kp2->string);
}

345
static BOOL TERM_AddKeyDescr(const char* string, const struct dbkey_descr* descr)
346
{
347
    if (!string || string == (const char*)-1) return TRUE;
348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
    if (!TERM_dbkey)
    {
        TERM_dbkey_size = 32;
        TERM_dbkey = HeapAlloc(GetProcessHeap(), 0, TERM_dbkey_size * sizeof(struct dbkey_pair));
        if (!TERM_dbkey) return FALSE;
    }
    if (TERM_dbkey_index == TERM_dbkey_size)
    {
        struct dbkey_pair*      new;

        new = HeapReAlloc(GetProcessHeap(), 0, TERM_dbkey, (2 * TERM_dbkey_size) * sizeof(struct dbkey_pair));
        if (!new) return FALSE;
        TERM_dbkey = new;
        TERM_dbkey_size *= 2;
    }
363 364 365
    TERM_dbkey[TERM_dbkey_index].string     = string;
    TERM_dbkey[TERM_dbkey_index].string_len = strlen(string);
    TERM_dbkey[TERM_dbkey_index].descr      = *descr;
366 367 368 369 370 371
    TERM_dbkey_index++;
    return TRUE;
}

static BOOL TERM_BuildKeyDB(void)
{
372
    unsigned i, j, len;
373 374 375
    struct dbkey_descr descr;
    char tmp[64];

376
    for (i = 0; i < ARRAY_SIZE(TERM_dbkey_init); i++)
377
    {
378
        if (!TERM_AddKeyDescr(tigetstr((char *)TERM_dbkey_init[i].string_normal), &TERM_dbkey_init[i].descr))
379
            return FALSE;
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
        if (TERM_dbkey_init[i].string_xterm)
        {
            descr = TERM_dbkey_init[i].descr;
            strcpy(tmp, TERM_dbkey_init[i].string_xterm);
            len = strlen(tmp);
            tmp[len + 1] = '\0';
#define X(v, f) do { tmp[len] = v; descr.p3 = (f); if (!TERM_AddKeyDescr(tigetstr(tmp), &descr)) return FALSE; } while (0)
            X('\0', SHIFT_PRESSED);
            X('3',  LEFT_ALT_PRESSED);
            X('4',  SHIFT_PRESSED | LEFT_ALT_PRESSED);
            X('5',  LEFT_CTRL_PRESSED);
            X('6',  LEFT_CTRL_PRESSED|SHIFT_PRESSED);
            X('7',  LEFT_CTRL_PRESSED|LEFT_ALT_PRESSED);
            X('8',  LEFT_CTRL_PRESSED|LEFT_ALT_PRESSED|SHIFT_PRESSED);
#undef X
        }
396
    }
397 398 399 400 401 402 403 404 405 406 407
    for (i = 0; i < TERM_dbkey_index; i++)
    {
        for (j = 0; j < TERM_dbkey_index; j++)
        {
            if (i != j &&
                TERM_dbkey[i].string_len >= TERM_dbkey[j].string_len &&
                !memcmp(TERM_dbkey[i].string, TERM_dbkey[j].string, TERM_dbkey[j].string_len))
                FIXME("substring %d/%s %d/%s\n", i, TERM_dbkey[i].string, j, TERM_dbkey[j].string);
        }
    }
    qsort(TERM_dbkey, TERM_dbkey_index, sizeof(TERM_dbkey[0]), TERM_dbkey_cmp);
408 409
    return TRUE;
}
410

411 412
static BOOL TERM_init_done /* = FALSE */;

413 414
BOOL TERM_Init(void)
{
415 416
    /* if we're not attached to a tty, don't fire the curses support */
    if (!isatty(0) || !isatty(1)) return FALSE;
417
    if (!getenv("TERM")) return FALSE;
418 419
    if (!TERM_bind_libcurses()) return FALSE;
    if (setupterm(NULL, 1 /* really ?? */, NULL) == -1) return FALSE;
420
    TERM_init_done = TRUE;
421 422
    TERM_BuildKeyDB();
    /* set application key mode */
423
    putp(tigetstr((char *)"smkx"));
424 425 426 427 428
    return TRUE;
}

BOOL TERM_Exit(void)
{
429 430 431
    if (TERM_init_done)
    {
        /* put back the cursor key mode */
432
        putp(tigetstr((char *)"rmkx"));
433
    }
434 435
    return TRUE;
}
436 437 438 439

/* -1 not found, 0 cannot decide, > 0 found */
int TERM_FillInputRecord(const char* in, size_t len, INPUT_RECORD* ir)
{
440 441
    int low = 0, high = TERM_dbkey_index - 1, mid, res;
    struct dbkey_descr* found;
442

443
    while (low <= high)
444
    {
445 446 447
        mid = low + (high - low) / 2;
        res = memcmp(in, TERM_dbkey[mid].string, len);
        if (!res)
448
        {
449 450 451 452 453 454 455 456 457 458 459 460
            if (len < TERM_dbkey[mid].string_len) return 0;
            found = &TERM_dbkey[mid].descr;
            switch (found->kind)
            {
            case dbk_simple:
                return TERM_FillSimpleChar(found->p1, ir);
            case dbk_complex:
                init_complex_char(&ir[0], 1, found->p1, found->p2, ENHANCED_KEY | found->p3);
                init_complex_char(&ir[1], 0, found->p1, found->p2, ENHANCED_KEY | found->p3);
                return 2;
            }
            return -1;
461
        }
462 463
        else if (res < 0) high = mid - 1;
        else low = mid + 1;
464 465 466 467
    }
    return -1;
}

468 469 470
#else
BOOL     TERM_Init(void) {return FALSE;}
BOOL     TERM_Exit(void) {return FALSE;}
471
int      TERM_FillInputRecord(const char* in, size_t len, INPUT_RECORD* ir) {return -1;}
472
#endif