From a392e9a10262e5f9cfbb284d38e6174cb6f4809a Mon Sep 17 00:00:00 2001 From: Eric Pouech Date: Tue, 18 Jan 2011 22:02:37 +0100 Subject: [PATCH] kernel32: Correctly parse the input strings for advanced keys. --- dlls/kernel32/console.c | 99 +++++++++++++++------ dlls/kernel32/console_private.h | 1 + dlls/kernel32/term.c | 149 ++++++++++++++++++++++++++++++++ 3 files changed, 222 insertions(+), 27 deletions(-) diff --git a/dlls/kernel32/console.c b/dlls/kernel32/console.c index c714e50c409..b7253f81676 100644 --- a/dlls/kernel32/console.c +++ b/dlls/kernel32/console.c @@ -5,7 +5,7 @@ * Copyright 1997 Karl Garrison * Copyright 1998 John Richardson * Copyright 1998 Marcus Meissner - * Copyright 2001,2002,2004,2005 Eric Pouech + * Copyright 2001,2002,2004,2005,2010 Eric Pouech * Copyright 2001 Alexandre Julliard * * This library is free software; you can redistribute it and/or @@ -1099,39 +1099,84 @@ enum read_console_input_return {rci_error = 0, rci_timeout = 1, rci_gotone = 2}; static enum read_console_input_return bare_console_fetch_input(HANDLE handle, int fd, DWORD timeout) { - struct pollfd pollfd; - char ch; - enum read_console_input_return ret; - unsigned numEvent; - INPUT_RECORD ir[8]; - DWORD written; + enum read_console_input_return ret; + char input[8]; + int i; + size_t idx = 0; + unsigned numEvent; + INPUT_RECORD ir[8]; + DWORD written; + struct pollfd pollfd; + BOOL locked = FALSE, next_char; - pollfd.fd = fd; - pollfd.events = POLLIN; - pollfd.revents = 0; - - switch (poll(&pollfd, 1, timeout)) + do { - case 1: - RtlEnterCriticalSection(&CONSOLE_CritSect); - switch (read(fd, &ch, 1)) + if (idx == sizeof(input)) + { + FIXME("buffer too small (%s)\n", wine_dbgstr_an(input, idx)); + ret = rci_error; + break; + } + pollfd.fd = fd; + pollfd.events = POLLIN; + pollfd.revents = 0; + next_char = FALSE; + + switch (poll(&pollfd, 1, timeout)) { case 1: - numEvent = TERM_FillSimpleChar(ch, ir); - ret = WriteConsoleInputW(handle, ir, numEvent, &written) ? rci_gotone : rci_error; + if (!locked) + { + RtlEnterCriticalSection(&CONSOLE_CritSect); + locked = TRUE; + } + i = read(fd, &input[idx], 1); + if (i < 0) + { + ret = rci_error; + break; + } + if (i == 0) + { + /* actually another thread likely beat us to reading the char + * return rci_gotone, while not perfect, it should work in most of the cases (as the new event + * should be now in the queue, fed from the other thread) + */ + ret = rci_gotone; + break; + } + + idx++; + numEvent = TERM_FillInputRecord(input, idx, ir); + switch (numEvent) + { + case 0: + /* we need more char(s) to tell if it matches a key-db entry. wait 1/2s for next char */ + timeout = 500; + next_char = TRUE; + break; + case -1: + /* we haven't found the string into key-db, push full input string into server */ + for (i = 0; i < idx; i++) + { + numEvent = TERM_FillSimpleChar(input[i], ir); + WriteConsoleInputW(handle, ir, numEvent, &written); + } + ret = idx == 0 ? rci_timeout : rci_gotone; + break; + default: + /* we got a transformation from key-db... push this into server */ + ret = WriteConsoleInputW(handle, ir, numEvent, &written) ? rci_gotone : rci_error; + break; + } break; - /* actually another thread likely beat us to reading the char - * return gotone, while not perfect, it should work in most of the cases (as the new event - * should be now in the queue) - */ - case 0: ret = rci_gotone; break; + case 0: ret = rci_timeout; break; default: ret = rci_error; break; } - RtlLeaveCriticalSection(&CONSOLE_CritSect); - return ret; - case 0: return rci_timeout; - default: return rci_error; - } + } while (next_char); + if (locked) RtlLeaveCriticalSection(&CONSOLE_CritSect); + + return ret; } static enum read_console_input_return read_console_input(HANDLE handle, PINPUT_RECORD ir, DWORD timeout) diff --git a/dlls/kernel32/console_private.h b/dlls/kernel32/console_private.h index c5b8799f132..b25f65c754d 100644 --- a/dlls/kernel32/console_private.h +++ b/dlls/kernel32/console_private.h @@ -37,5 +37,6 @@ extern WCHAR* CONSOLE_Readline(HANDLE, BOOL); extern BOOL TERM_Init(void); extern BOOL TERM_Exit(void); extern unsigned TERM_FillSimpleChar(unsigned real_inchar, INPUT_RECORD* ir); +extern int TERM_FillInputRecord(const char* in, size_t len, INPUT_RECORD* ir); #endif /* __WINE_CONSOLE_PRIVATE_H */ diff --git a/dlls/kernel32/term.c b/dlls/kernel32/term.c index acffd43871d..8111f0b1b3a 100644 --- a/dlls/kernel32/term.c +++ b/dlls/kernel32/term.c @@ -157,7 +157,10 @@ static void *nc_handle = NULL; #define MAKE_FUNCPTR(f) static typeof(f) * p_##f; +MAKE_FUNCPTR(putp) MAKE_FUNCPTR(setupterm) +MAKE_FUNCPTR(tigetstr) +MAKE_FUNCPTR(tparm) #undef MAKE_FUNCPTR @@ -185,7 +188,10 @@ static BOOL TERM_bind_libcurses(void) goto sym_not_found; \ } + LOAD_FUNCPTR(putp) LOAD_FUNCPTR(setupterm) + LOAD_FUNCPTR(tigetstr) + LOAD_FUNCPTR(tparm) #undef LOAD_FUNCPTR @@ -201,20 +207,163 @@ sym_not_found: return FALSE; } +#define putp p_putp #define setupterm p_setupterm +#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; +}; + +struct dbkey_pair +{ + const char* string; + struct dbkey_descr descr; +}; + +static struct dbkey_pair TERM_dbkey_init[] = { + {"kcud1", {dbk_complex, 0x50, 0x28, 0}}, + {"kcuu1", {dbk_complex, 0x48, 0x26, 0}}, + {"kcub1", {dbk_complex, 0x4b, 0x25, 0}}, + {"kcuf1", {dbk_complex, 0x4d, 0x27, 0}}, + {"khome", {dbk_complex, 0x47, 0x24, 0}}, + {"kbs", {dbk_simple, 0x7f, 0x00, 0}}, + {"kf1", {dbk_complex, 0x3b, 0x70, 0}}, + {"kf2", {dbk_complex, 0x3c, 0x71, 0}}, + {"kf3", {dbk_complex, 0x3d, 0x72, 0}}, + {"kf4", {dbk_complex, 0x3e, 0x73, 0}}, + {"kf5", {dbk_complex, 0x3f, 0x74, 0}}, + {"kf6", {dbk_complex, 0x40, 0x75, 0}}, + {"kf7", {dbk_complex, 0x41, 0x76, 0}}, + {"kf8", {dbk_complex, 0x42, 0x77, 0}}, + {"kf9", {dbk_complex, 0x43, 0x78, 0}}, + {"kf10", {dbk_complex, 0x44, 0x79, 0}}, + {"kf11", {dbk_complex, 0xd9, 0x7a, 0}}, + {"kf12", {dbk_complex, 0xda, 0x7b, 0}}, + {"kdch1", {dbk_complex, 0x53, 0x2e, 0}}, + {"kich1", {dbk_complex, 0x52, 0x2d, 0}}, + {"knp", {dbk_complex, 0x51, 0x22, 0}}, + {"kpp", {dbk_complex, 0x49, 0x21, 0}}, + {"kcbt", {dbk_simple, 0x09, 0x00, SHIFT_PRESSED}}, + + {"kend", {dbk_complex, 0x4f, 0x23, 0}}, + /* {"kmous", NULL, }, */ + {"kDC", {dbk_complex, 0x53, 0x2e, SHIFT_PRESSED}}, + {"kEND", {dbk_complex, 0x4f, 0x23, SHIFT_PRESSED}}, + {"kHOM", {dbk_complex, 0x47, 0x24, SHIFT_PRESSED}}, + {"kIC", {dbk_complex, 0x52, 0x2d, SHIFT_PRESSED}}, + {"kLFT", {dbk_complex, 0x4b, 0x25, SHIFT_PRESSED}}, + {"kRIT", {dbk_complex, 0x4d, 0x27, SHIFT_PRESSED}}, + + /* 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 + */ +}; + +static struct dbkey_pair* TERM_dbkey; +static unsigned TERM_dbkey_size; +static unsigned TERM_dbkey_index; + +static BOOL TERM_AddKeyDescr(const char* string, struct dbkey_descr* descr) +{ + if (!string) return FALSE; + 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; + } + TERM_dbkey[TERM_dbkey_index].string = string; + TERM_dbkey[TERM_dbkey_index].descr = *descr; + TERM_dbkey_index++; + return TRUE; +} + +static BOOL TERM_BuildKeyDB(void) +{ + unsigned i; + for (i = 0; i < sizeof(TERM_dbkey_init) / sizeof(TERM_dbkey_init[0]); i++) + { + if (!TERM_AddKeyDescr(tigetstr(TERM_dbkey_init[i].string), &TERM_dbkey_init[i].descr)) + return FALSE; + } + return TRUE; +} BOOL TERM_Init(void) { if (!TERM_bind_libcurses()) return FALSE; if (setupterm(NULL, 1 /* really ?? */, NULL) == -1) return FALSE; + TERM_BuildKeyDB(); + /* set application key mode */ + putp(tigetstr("smkx")); return TRUE; } BOOL TERM_Exit(void) { + /* put back the cursor key mode */ + putp(tigetstr("rmkx")); return TRUE; } + +/* -1 not found, 0 cannot decide, > 0 found */ +int TERM_FillInputRecord(const char* in, size_t len, INPUT_RECORD* ir) +{ + unsigned i; + struct dbkey_descr* found = NULL; + + for (i = 0; i < TERM_dbkey_index; i++) + { + if (!memcmp(TERM_dbkey[i].string, in, len)) + { + if (len < strlen(TERM_dbkey[i].string)) return 0; + if (found) return 0; + found = &TERM_dbkey[i].descr; + } + } + if (!found) return -1; + 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; +} + #else BOOL TERM_Init(void) {return FALSE;} BOOL TERM_Exit(void) {return FALSE;} +int TERM_FillInputRecord(const char* in, INPUT_RECORD* ir) {return -1;} #endif