From 8747626b6aa845718c51ab130692338647c187a4 Mon Sep 17 00:00:00 2001 From: bagajjal Date: Wed, 27 Sep 2017 12:16:41 -0700 Subject: [PATCH] Multiple terminal related fixes Fix the bugs - PowerShell/Win32-OpenSSH#845 PowerShell/Win32-OpenSSH#865 PowerShell/Win32-OpenSSH#885 PowerShell/Win32-OpenSSH#886 Refer to the below URL https://github.com/mintty/mintty/wiki/Keycodes#Editing_keys.md --- contrib/win32/win32compat/shell-host.c | 149 ++++++++++++++++++---- contrib/win32/win32compat/tncon.c | 167 +++++++++++++++++++++++-- contrib/win32/win32compat/tnnet.c | 7 +- 3 files changed, 288 insertions(+), 35 deletions(-) diff --git a/contrib/win32/win32compat/shell-host.c b/contrib/win32/win32compat/shell-host.c index 00aa49606..2f731da4c 100644 --- a/contrib/win32/win32compat/shell-host.c +++ b/contrib/win32/win32compat/shell-host.c @@ -81,6 +81,19 @@ #define VK_X 0x58 #define VK_Y 0x59 #define VK_Z 0x5A +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 + +const int MAX_CTRL_SEQ_LEN = 7; +const int MIN_CTRL_SEQ_LEN = 6; typedef BOOL(WINAPI *__t_SetCurrentConsoleFontEx)( _In_ HANDLE hConsoleOutput, @@ -115,7 +128,7 @@ typedef struct consoleEvent { } consoleEvent; struct key_translation { - wchar_t in[6]; + wchar_t in[8]; int vk; wchar_t out; int in_key_len; @@ -124,10 +137,10 @@ struct key_translation { /* All the substrings should be in the end, otherwise ProcessIncomingKeys() will not work as expected */ struct key_translation keys[] = { - { L"\r", VK_RETURN, L'\r' , 0 , 0}, - { L"\n", VK_RETURN, L'\r' , 0 , 0 }, - { L"\b", VK_BACK, L'\b' , 0 , 0 }, - { L"\x7f", VK_BACK, L'\b' , 0 , 0 }, + { L"\r", VK_RETURN, L'\r', 0, 0}, + { L"\n", VK_RETURN, L'\r', 0, 0 }, + { L"\b", VK_BACK, L'\b', 0, 0 }, + { L"\x7f", VK_BACK, L'\b', 0 , 0 }, { L"\t", VK_TAB, L'\t' , 0 , 0}, { L"\x1b[A", VK_UP, 0 , 0 , 0}, { L"\x1b[B", VK_DOWN, 0 , 0 , 0}, @@ -189,7 +202,53 @@ struct key_translation keys[] = { { L"\x17", VK_W, L'\x17' , 0 , LEFT_CTRL_PRESSED}, { L"\x18", VK_X, L'\x18' , 0 , LEFT_CTRL_PRESSED}, { L"\x19", VK_Y, L'\x19' , 0 , LEFT_CTRL_PRESSED}, - { L"\x1A", VK_Z, L'\x1A' , 0 , LEFT_CTRL_PRESSED} + { L"\x1A", VK_Z, L'\x1A' , 0 , LEFT_CTRL_PRESSED}, + { L"\033a", VK_A, L'a', 0, LEFT_ALT_PRESSED}, + { L"\033b", VK_B, L'b', 0, LEFT_ALT_PRESSED}, + { L"\033c", VK_C, L'c', 0, LEFT_ALT_PRESSED}, + { L"\033d", VK_D, L'd', 0, LEFT_ALT_PRESSED}, + { L"\033e", VK_E, L'e', 0, LEFT_ALT_PRESSED}, + { L"\033f", VK_F, L'f', 0, LEFT_ALT_PRESSED}, + { L"\033g", VK_G, L'g', 0, LEFT_ALT_PRESSED}, + { L"\033h", VK_H, L'h', 0, LEFT_ALT_PRESSED}, + { L"\033i", VK_I, L'i', 0, LEFT_ALT_PRESSED}, + { L"\033j", VK_J, L'j', 0, LEFT_ALT_PRESSED}, + { L"\033k", VK_K, L'k', 0, LEFT_ALT_PRESSED}, + { L"\033l", VK_L, L'l', 0, LEFT_ALT_PRESSED}, + { L"\033m", VK_M, L'm', 0, LEFT_ALT_PRESSED}, + { L"\033n", VK_N, L'n', 0, LEFT_ALT_PRESSED}, + { L"\033o", VK_O, L'o', 0, LEFT_ALT_PRESSED}, + { L"\033p", VK_P, L'p', 0, LEFT_ALT_PRESSED}, + { L"\033q", VK_Q, L'q', 0, LEFT_ALT_PRESSED}, + { L"\033r", VK_R, L'r', 0, LEFT_ALT_PRESSED}, + { L"\033s", VK_S, L's', 0, LEFT_ALT_PRESSED}, + { L"\033t", VK_T, L't', 0, LEFT_ALT_PRESSED}, + { L"\033u", VK_U, L'u', 0, LEFT_ALT_PRESSED}, + { L"\033v", VK_V, L'v', 0, LEFT_ALT_PRESSED}, + { L"\033w", VK_W, L'w', 0, LEFT_ALT_PRESSED}, + { L"\033x", VK_X, L'x', 0, LEFT_ALT_PRESSED}, + { L"\033y", VK_Y, L'y', 0, LEFT_ALT_PRESSED}, + { L"\033z", VK_Z, L'z', 0, LEFT_ALT_PRESSED}, + { L"\0330", VK_0, L'0', 0, LEFT_ALT_PRESSED}, + { L"\0331", VK_1, L'1', 0, LEFT_ALT_PRESSED}, + { L"\0332", VK_2, L'2', 0, LEFT_ALT_PRESSED}, + { L"\0333", VK_3, L'3', 0, LEFT_ALT_PRESSED}, + { L"\0334", VK_4, L'4', 0, LEFT_ALT_PRESSED}, + { L"\0335", VK_5, L'5', 0, LEFT_ALT_PRESSED}, + { L"\0336", VK_6, L'6', 0, LEFT_ALT_PRESSED}, + { L"\0337", VK_7, L'7', 0, LEFT_ALT_PRESSED}, + { L"\0338", VK_8, L'8', 0, LEFT_ALT_PRESSED}, + { L"\0339", VK_9, L'9', 0, LEFT_ALT_PRESSED}, + { L"\033!", VK_1, L'!', 0, LEFT_ALT_PRESSED | SHIFT_PRESSED }, + { L"\033@", VK_2, L'@', 0, LEFT_ALT_PRESSED | SHIFT_PRESSED }, + { L"\033#", VK_3, L'#', 0, LEFT_ALT_PRESSED | SHIFT_PRESSED }, + { L"\033$", VK_4, L'$', 0, LEFT_ALT_PRESSED | SHIFT_PRESSED }, + { L"\033%", VK_5, L'%', 0, LEFT_ALT_PRESSED | SHIFT_PRESSED }, + { L"\033^", VK_6, L'^', 0, LEFT_ALT_PRESSED | SHIFT_PRESSED }, + { L"\033&", VK_7, L'&', 0, LEFT_ALT_PRESSED | SHIFT_PRESSED }, + { L"\033*", VK_8, L'*', 0, LEFT_ALT_PRESSED | SHIFT_PRESSED }, + { L"\033(", VK_9, L'(', 0, LEFT_ALT_PRESSED | SHIFT_PRESSED }, + { L"\033)", VK_0, L')', 0, LEFT_ALT_PRESSED | SHIFT_PRESSED } }; static SHORT lastX = 0; @@ -335,11 +394,19 @@ initialize_keylen() } int -ProcessCtrlSequence(wchar_t *buf, int buf_len) +ProcessModifierKeySequence(wchar_t *buf, int buf_len) { - int vkey = 0; - /* Decode special keys when pressed CTRL key */ - if (buf[0] == L'\033' && buf[1] == L'[' && buf[buf_len - 3] == L';' && buf[buf_len - 2] == L'5') { + if(buf_len < MIN_CTRL_SEQ_LEN) + return 0; + + int vkey = 0; + int modifier_key = _wtoi((wchar_t *)&buf[buf_len - 2]); + + if ((modifier_key < 2) && (modifier_key > 7)) + return 0; + + /* Decode special keys when pressed ALT/CTRL/SHIFT key */ + if (buf[0] == L'\033' && buf[1] == L'[' && buf[buf_len - 3] == L';') { if (buf[buf_len - 1] == L'~') { /* VK_DELETE, VK_PGDN, VK_PGUP */ if (!vkey && buf_len == 6) @@ -357,18 +424,51 @@ ProcessCtrlSequence(wchar_t *buf, int buf_len) if (!vkey && buf_len == 6 && buf[2] == L'1' && isalpha(buf[5])) vkey = GetVirtualKeyByMask(L'O', &buf[5], 1, 0); } - if (vkey) - SendKeyStroke(child_in, vkey, 0, LEFT_CTRL_PRESSED); + if (vkey) { + switch (modifier_key) + { + case 2: + SendKeyStroke(child_in, vkey, 0, SHIFT_PRESSED); + break; + case 3: + SendKeyStroke(child_in, vkey, 0, LEFT_ALT_PRESSED); + break; + case 4: + SendKeyStroke(child_in, vkey, 0, SHIFT_PRESSED | LEFT_ALT_PRESSED); + break; + case 5: + SendKeyStroke(child_in, vkey, 0, LEFT_CTRL_PRESSED); + break; + case 6: + SendKeyStroke(child_in, vkey, 0, SHIFT_PRESSED | LEFT_CTRL_PRESSED); + break; + case 7: + SendKeyStroke(child_in, vkey, 0, LEFT_CTRL_PRESSED | LEFT_ALT_PRESSED); + break; + } + } + } return vkey; } +int +CheckKeyTranslations(wchar_t *buf, int buf_len, int *index) +{ + for (int j = 0; j < ARRAYSIZE(keys); j++) { + if ((buf_len >= keys[j].in_key_len) && (wcsncmp(buf, keys[j].in, keys[j].in_key_len) == 0)) { + *index = j; + return 1; + } + } + + return 0; +} void ProcessIncomingKeys(char * ansikey) { int buf_len = 0; - const int MAX_CTRL_SEQ_LEN = 7; const wchar_t *ESC_SEQ = L"\x1b"; wchar_t *buf = utf8_to_utf16(ansikey); @@ -379,26 +479,33 @@ ProcessIncomingKeys(char * ansikey) loop: while (buf && ((buf_len=(int)wcslen(buf)) > 0)) { - for (int j = 0; j < ARRAYSIZE(keys); j++) { - if ( (buf_len >= keys[j].in_key_len) && (wcsncmp(buf, keys[j].in, keys[j].in_key_len) == 0) ) { - SendKeyStroke(child_in, keys[j].vk, keys[j].out, keys[j].ctrlState); - buf += keys[j].in_key_len; - goto loop; - } + int j = 0; + if (CheckKeyTranslations(buf, buf_len, &j)) { + SendKeyStroke(child_in, keys[j].vk, keys[j].out, keys[j].ctrlState); + buf += keys[j].in_key_len; + goto loop; } /* Decode special keys when pressed CTRL key. CTRL sequences can be of size 6 or 7. */ - if ((buf_len >= MAX_CTRL_SEQ_LEN) && ProcessCtrlSequence(buf, MAX_CTRL_SEQ_LEN)) { + if ((buf_len >= MAX_CTRL_SEQ_LEN) && ProcessModifierKeySequence(buf, MAX_CTRL_SEQ_LEN)) { buf += MAX_CTRL_SEQ_LEN; goto loop; } - if ((buf_len >= (MAX_CTRL_SEQ_LEN - 1)) && ProcessCtrlSequence(buf, MAX_CTRL_SEQ_LEN - 1)) { + if ((buf_len >= (MAX_CTRL_SEQ_LEN - 1)) && ProcessModifierKeySequence(buf, MAX_CTRL_SEQ_LEN - 1)) { buf += (MAX_CTRL_SEQ_LEN - 1); goto loop; } if(wcsncmp(buf, ESC_SEQ, wcslen(ESC_SEQ)) == 0) { + wchar_t* p = buf + wcslen(ESC_SEQ); + /* Alt sequence */ + if (CheckKeyTranslations(p, buf_len - (int)wcslen(ESC_SEQ), &j) && !(keys[j].ctrlState & LEFT_ALT_PRESSED)) { + SendKeyStroke(child_in, keys[j].vk, keys[j].out, keys[j].ctrlState| LEFT_ALT_PRESSED); + buf += wcslen(ESC_SEQ) +keys[j].in_key_len; + goto loop; + } + SendKeyStroke(child_in, VK_ESCAPE, L'\x1b', 0); buf += wcslen(ESC_SEQ); goto loop; diff --git a/contrib/win32/win32compat/tncon.c b/contrib/win32/win32compat/tncon.c index 3b3a396bf..ce0be0492 100644 --- a/contrib/win32/win32compat/tncon.c +++ b/contrib/win32/win32compat/tncon.c @@ -50,6 +50,7 @@ extern int ScreenY; extern int ScreenX; extern int ScrollTop; extern int ScrollBottom; +unsigned char tmp_buf[30]; /* terminal global switches*/ TelParams Parameters = { @@ -102,6 +103,27 @@ DataAvailable(HANDLE h) return FALSE; } +int +GetModifierKey(DWORD dwControlKeyState) +{ + int modKey = 0; + if ((dwControlKeyState & LEFT_ALT_PRESSED) || (dwControlKeyState & RIGHT_ALT_PRESSED)) + modKey += 2; + + if (dwControlKeyState & SHIFT_PRESSED) + modKey += 1; + + if ((dwControlKeyState & LEFT_CTRL_PRESSED) || (dwControlKeyState & RIGHT_CTRL_PRESSED)) + modKey += 4; + + if (modKey){ + memset(tmp_buf, 0, sizeof(tmp_buf)); + modKey++; + } + + return modKey; +} + int ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen) { @@ -110,11 +132,12 @@ ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen) DWORD dwInput = 0; DWORD dwControlKeyState = 0; DWORD rc = 0; - unsigned char octets[20]; + unsigned char octets[20]; char aChar = 0; INPUT_RECORD InputRecord; BOOL bCapsOn = FALSE; BOOL bShift = FALSE; + int modKey = 0; glob_out = destin; glob_space = destinlen; @@ -138,6 +161,7 @@ ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen) bShift = (InputRecord.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED); dwControlKeyState = InputRecord.Event.KeyEvent.dwControlKeyState & ~(CAPSLOCK_ON | ENHANCED_KEY | NUMLOCK_ON | SCROLLLOCK_ON); + modKey = GetModifierKey(dwControlKeyState); if (InputRecord.Event.KeyEvent.bKeyDown) { int n = WideCharToMultiByte( CP_UTF8, @@ -167,34 +191,144 @@ ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen) default: switch (InputRecord.Event.KeyEvent.wVirtualKeyCode) { case VK_UP: - NetWriteString2(pParams->Socket, (char *)(gbVTAppMode ? APP_UP_ARROW : UP_ARROW), 3, 0); + if(!modKey) + NetWriteString2(pParams->Socket, (char *)(gbVTAppMode ? APP_UP_ARROW : UP_ARROW), 3, 0); + else { + /* ^[[1;mA */ + char *p = "\033[1;"; + strcpy_s(tmp_buf, sizeof(tmp_buf), p); + size_t index = strlen(p); + tmp_buf[index++] = modKey + '0'; + tmp_buf[index] = 'A'; + + NetWriteString2(pParams->Socket, tmp_buf, index+1, 0); + } break; case VK_DOWN: - NetWriteString2(pParams->Socket, (char *)(gbVTAppMode ? APP_DOWN_ARROW : DOWN_ARROW), 3, 0); + if(!modKey) + NetWriteString2(pParams->Socket, (char *)(gbVTAppMode ? APP_DOWN_ARROW : DOWN_ARROW), 3, 0); + else { + /* ^[[1;mB */ + char *p = "\033[1;"; + strcpy_s(tmp_buf, sizeof(tmp_buf), p); + size_t index = strlen(p); + tmp_buf[index++] = modKey + '0'; + tmp_buf[index] = 'B'; + + NetWriteString2(pParams->Socket, tmp_buf, index+1, 0); + } break; case VK_RIGHT: - NetWriteString2(pParams->Socket, (char *)(gbVTAppMode ? APP_RIGHT_ARROW : RIGHT_ARROW), 3, 0); + if(!modKey) + NetWriteString2(pParams->Socket, (char *)(gbVTAppMode ? APP_RIGHT_ARROW : RIGHT_ARROW), 3, 0); + else { + /* ^[[1;mC */ + char *p = "\033[1;"; + strcpy_s(tmp_buf, sizeof(tmp_buf), p); + size_t index = strlen(p); + tmp_buf[index++] = modKey + '0'; + tmp_buf[index] = 'C'; + + NetWriteString2(pParams->Socket, tmp_buf, index+1, 0); + } break; case VK_LEFT: - NetWriteString2(pParams->Socket, (char *)(gbVTAppMode ? APP_LEFT_ARROW : LEFT_ARROW), 3, 0); + if(!modKey) + NetWriteString2(pParams->Socket, (char *)(gbVTAppMode ? APP_LEFT_ARROW : LEFT_ARROW), 3, 0); + else { + /* ^[[1;mD */ + char *p = "\033[1;"; + strcpy_s(tmp_buf, sizeof(tmp_buf), p); + size_t index = strlen(p); + tmp_buf[index++] = modKey + '0'; + tmp_buf[index] = 'D'; + + NetWriteString2(pParams->Socket, tmp_buf, index+1, 0); + } break; case VK_END: - NetWriteString2(pParams->Socket, (char *)SELECT_KEY, 4, 0); + if(!modKey) + NetWriteString2(pParams->Socket, (char *)SELECT_KEY, 4, 0); + else { + /* ^[[1;mF */ + char *p = "\033[1;"; + strcpy_s(tmp_buf, sizeof(tmp_buf), p); + size_t index = strlen(p); + tmp_buf[index++] = modKey + '0'; + tmp_buf[index] = 'F'; + + NetWriteString2(pParams->Socket, tmp_buf, index+1, 0); + } break; case VK_HOME: - NetWriteString2(pParams->Socket, (char *)FIND_KEY, 4, 0); + if(!modKey) + NetWriteString2(pParams->Socket, (char *)FIND_KEY, 4, 0); + else { + /* ^[[1;mH */ + char *p = "\033[1;"; + strcpy_s(tmp_buf, sizeof(tmp_buf), p); + size_t index = strlen(p); + tmp_buf[index++] = modKey + '0'; + tmp_buf[index] = 'H'; + + NetWriteString2(pParams->Socket, tmp_buf, index+1, 0); + } break; case VK_INSERT: - NetWriteString2(pParams->Socket, (char *)INSERT_KEY, 4, 0); + if(!modKey) + NetWriteString2(pParams->Socket, (char *)INSERT_KEY, 4, 0); + else { + /* ^[[2;m~ */ + char *p = "\033[2;"; + strcpy_s(tmp_buf, sizeof(tmp_buf), p); + size_t index = strlen(p); + tmp_buf[index++] = modKey + '0'; + tmp_buf[index] = '~'; + + NetWriteString2(pParams->Socket, tmp_buf, index+1, 0); + } break; case VK_DELETE: - NetWriteString2(pParams->Socket, (char *)REMOVE_KEY, 4, 0); + if(!modKey) + NetWriteString2(pParams->Socket, (char *)REMOVE_KEY, 4, 0); + else { + /* ^[[3;m~ */ + char *p = "\033[3;"; + strcpy_s(tmp_buf, sizeof(tmp_buf), p); + size_t index = strlen(p); + tmp_buf[index++] = modKey + '0'; + tmp_buf[index] = '~'; + + NetWriteString2(pParams->Socket, tmp_buf, index+1, 0); + } break; case VK_PRIOR: /* page up */ - NetWriteString2(pParams->Socket, (char *)PREV_KEY, 4, 0); + if (!modKey) + NetWriteString2(pParams->Socket, (char *)PREV_KEY, 4, 0); + else { + /* ^[[5;m~ */ + char *p = "\033[5;"; + strcpy_s(tmp_buf, sizeof(tmp_buf), p); + size_t index = strlen(p); + tmp_buf[index++] = modKey + '0'; + tmp_buf[index] = '~'; + + NetWriteString2(pParams->Socket, tmp_buf, index+1, 0); + } break; case VK_NEXT: /* page down */ - NetWriteString2(pParams->Socket, (char *)NEXT_KEY, 4, 0); + if(!modKey) + NetWriteString2(pParams->Socket, (char *)NEXT_KEY, 4, 0); + else { + /* ^[[6;m~ */ + char *p = "\033[6;"; + strcpy_s(tmp_buf, sizeof(tmp_buf), p); + size_t index = strlen(p); + tmp_buf[index++] = modKey + '0'; + tmp_buf[index] = '~'; + + NetWriteString2(pParams->Socket, tmp_buf, index+1, 0); + } break; case VK_BACK: NetWriteString2(pParams->Socket, (char *)BACKSPACE_KEY, 1, 0); @@ -574,7 +708,16 @@ ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen) NetWriteString2(pParams->Socket, (char *)SHIFT_CTRL_PF12_KEY, strlen(SHIFT_CTRL_PF12_KEY), 0); break; default: - NetWriteString2(pParams->Socket, (char *)octets, n, 0); + if (strcmp((char *) octets, "")) { + if ((dwControlKeyState & LEFT_ALT_PRESSED) || (dwControlKeyState & RIGHT_ALT_PRESSED)) { + memset(tmp_buf, 0, sizeof(tmp_buf)); + tmp_buf[0] = '\x1b'; + memcpy(tmp_buf + 1, (char *)octets, n); + NetWriteString2(pParams->Socket, tmp_buf, n + 1, 0); + } + else + NetWriteString2(pParams->Socket, (char *)octets, n, 0); + } break; } } diff --git a/contrib/win32/win32compat/tnnet.c b/contrib/win32/win32compat/tnnet.c index 5c53ebeb4..244e72667 100644 --- a/contrib/win32/win32compat/tnnet.c +++ b/contrib/win32/win32compat/tnnet.c @@ -84,8 +84,11 @@ processBuffer(HANDLE handle, char *buf, size_t len, unsigned char **respbuf, siz /* Console has the capability to parse so pass the raw buffer to console directly */ ConRestoreViewRect(); /* Restore the visible window, otherwise WriteConsoleW() gets messy */ wchar_t* t = utf8_to_utf16(buf); - WriteConsoleW(handle, t, (DWORD)wcslen(t), 0, 0); - free(t); + if (t) { + WriteConsoleW(handle, t, (DWORD)wcslen(t), 0, 0); + free(t); + } + ConSaveViewRect(); return; }