+/* This function gets the actual width of a character in the normal font.
+ */
+int CharWidth(Context ctx, int uc) {
+ HDC hdc = ctx;
+ int ibuf = 0;
+
+ /* If the font max is the same as the font ave width then this
+ * function is a no-op.
+ */
+ if (!font_dualwidth) return 1;
+
+ switch (uc & CSET_MASK) {
+ case ATTR_ASCII:
+ uc = unitab_line[uc & 0xFF];
+ break;
+ case ATTR_LINEDRW:
+ uc = unitab_xterm[uc & 0xFF];
+ break;
+ case ATTR_SCOACS:
+ uc = unitab_scoacs[uc & 0xFF];
+ break;
+ }
+ if (DIRECT_FONT(uc)) {
+ if (dbcs_screenfont) return 1;
+
+ /* Speedup, I know of no font where ascii is the wrong width */
+ if ((uc&CHAR_MASK) >= ' ' && (uc&CHAR_MASK)<= '~')
+ return 1;
+
+ if ( (uc & CSET_MASK) == ATTR_ACP ) {
+ SelectObject(hdc, fonts[FONT_NORMAL]);
+ } else if ( (uc & CSET_MASK) == ATTR_OEMCP ) {
+ another_font(FONT_OEM);
+ if (!fonts[FONT_OEM]) return 0;
+
+ SelectObject(hdc, fonts[FONT_OEM]);
+ } else
+ return 0;
+
+ if ( GetCharWidth32(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1 &&
+ GetCharWidth(hdc, uc&CHAR_MASK, uc&CHAR_MASK, &ibuf) != 1)
+ return 0;
+ } else {
+ /* Speedup, I know of no font where ascii is the wrong width */
+ if (uc >= ' ' && uc <= '~') return 1;
+
+ SelectObject(hdc, fonts[FONT_NORMAL]);
+ if ( GetCharWidth32W(hdc, uc, uc, &ibuf) == 1 )
+ /* Okay that one worked */ ;
+ else if ( GetCharWidthW(hdc, uc, uc, &ibuf) == 1 )
+ /* This should work on 9x too, but it's "less accurate" */ ;
+ else
+ return 0;
+ }
+
+ ibuf += font_width / 2 -1;
+ ibuf /= font_width;
+
+ return ibuf;
+}
+
+/*
+ * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
+ * codes. Returns number of bytes used or zero to drop the message
+ * or -1 to forward the message to windows.
+ */
+static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam,
+ unsigned char *output)
+{
+ BYTE keystate[256];
+ int scan, left_alt = 0, key_down, shift_state;
+ int r, i, code;
+ unsigned char *p = output;
+ static int alt_sum = 0;
+
+ HKL kbd_layout = GetKeyboardLayout(0);
+
+ static WORD keys[3];
+ static int compose_char = 0;
+ static WPARAM compose_key = 0;
+
+ r = GetKeyboardState(keystate);
+ if (!r)
+ memset(keystate, 0, sizeof(keystate));
+ else {
+#if 0
+#define SHOW_TOASCII_RESULT
+ { /* Tell us all about key events */
+ static BYTE oldstate[256];
+ static int first = 1;
+ static int scan;
+ int ch;
+ if (first)
+ memcpy(oldstate, keystate, sizeof(oldstate));
+ first = 0;
+
+ if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) {
+ debug(("+"));
+ } else if ((HIWORD(lParam) & KF_UP)
+ && scan == (HIWORD(lParam) & 0xFF)) {
+ debug((". U"));
+ } else {
+ debug((".\n"));
+ if (wParam >= VK_F1 && wParam <= VK_F20)
+ debug(("K_F%d", wParam + 1 - VK_F1));
+ else
+ switch (wParam) {
+ case VK_SHIFT:
+ debug(("SHIFT"));
+ break;
+ case VK_CONTROL:
+ debug(("CTRL"));
+ break;
+ case VK_MENU:
+ debug(("ALT"));
+ break;
+ default:
+ debug(("VK_%02x", wParam));
+ }
+ if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP)
+ debug(("*"));
+ debug((", S%02x", scan = (HIWORD(lParam) & 0xFF)));
+
+ ch = MapVirtualKeyEx(wParam, 2, kbd_layout);
+ if (ch >= ' ' && ch <= '~')
+ debug((", '%c'", ch));
+ else if (ch)
+ debug((", $%02x", ch));
+
+ if (keys[0])
+ debug((", KB0=%02x", keys[0]));
+ if (keys[1])
+ debug((", KB1=%02x", keys[1]));
+ if (keys[2])
+ debug((", KB2=%02x", keys[2]));
+
+ if ((keystate[VK_SHIFT] & 0x80) != 0)
+ debug((", S"));
+ if ((keystate[VK_CONTROL] & 0x80) != 0)
+ debug((", C"));
+ if ((HIWORD(lParam) & KF_EXTENDED))
+ debug((", E"));
+ if ((HIWORD(lParam) & KF_UP))
+ debug((", U"));
+ }
+
+ if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT);
+ else if ((HIWORD(lParam) & KF_UP))
+ oldstate[wParam & 0xFF] ^= 0x80;
+ else
+ oldstate[wParam & 0xFF] ^= 0x81;
+
+ for (ch = 0; ch < 256; ch++)
+ if (oldstate[ch] != keystate[ch])
+ debug((", M%02x=%02x", ch, keystate[ch]));
+
+ memcpy(oldstate, keystate, sizeof(oldstate));
+ }
+#endif
+
+ if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED)) {
+ keystate[VK_RMENU] = keystate[VK_MENU];
+ }
+
+
+ /* Nastyness with NUMLock - Shift-NUMLock is left alone though */
+ if ((cfg.funky_type == 3 ||
+ (cfg.funky_type <= 1 && term->app_keypad_keys &&
+ !cfg.no_applic_k))
+ && wParam == VK_NUMLOCK && !(keystate[VK_SHIFT] & 0x80)) {
+
+ wParam = VK_EXECUTE;
+
+ /* UnToggle NUMLock */
+ if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0)
+ keystate[VK_NUMLOCK] ^= 1;
+ }
+
+ /* And write back the 'adjusted' state */
+ SetKeyboardState(keystate);
+ }
+
+ /* Disable Auto repeat if required */
+ if (term->repeat_off &&
+ (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT)
+ return 0;
+
+ if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0)
+ left_alt = 1;
+
+ key_down = ((HIWORD(lParam) & KF_UP) == 0);
+
+ /* Make sure Ctrl-ALT is not the same as AltGr for ToAscii unless told. */
+ if (left_alt && (keystate[VK_CONTROL] & 0x80)) {
+ if (cfg.ctrlaltkeys)
+ keystate[VK_MENU] = 0;
+ else {
+ keystate[VK_RMENU] = 0x80;
+ left_alt = 0;
+ }
+ }
+
+ scan = (HIWORD(lParam) & (KF_UP | KF_EXTENDED | 0xFF));
+ shift_state = ((keystate[VK_SHIFT] & 0x80) != 0)
+ + ((keystate[VK_CONTROL] & 0x80) != 0) * 2;
+
+ /* Note if AltGr was pressed and if it was used as a compose key */
+ if (!compose_state) {
+ compose_key = 0x100;
+ if (cfg.compose_key) {
+ if (wParam == VK_MENU && (HIWORD(lParam) & KF_EXTENDED))
+ compose_key = wParam;
+ }
+ if (wParam == VK_APPS)
+ compose_key = wParam;
+ }
+
+ if (wParam == compose_key) {
+ if (compose_state == 0
+ && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == 0) compose_state =
+ 1;
+ else if (compose_state == 1 && (HIWORD(lParam) & KF_UP))
+ compose_state = 2;
+ else
+ compose_state = 0;
+ } else if (compose_state == 1 && wParam != VK_CONTROL)
+ compose_state = 0;
+
+ if (compose_state > 1 && left_alt)
+ compose_state = 0;
+
+ /* Sanitize the number pad if not using a PC NumPad */
+ if (left_alt || (term->app_keypad_keys && !cfg.no_applic_k
+ && cfg.funky_type != 2)
+ || cfg.funky_type == 3 || cfg.nethack_keypad || compose_state) {
+ if ((HIWORD(lParam) & KF_EXTENDED) == 0) {
+ int nParam = 0;
+ switch (wParam) {
+ case VK_INSERT:
+ nParam = VK_NUMPAD0;
+ break;
+ case VK_END:
+ nParam = VK_NUMPAD1;
+ break;
+ case VK_DOWN:
+ nParam = VK_NUMPAD2;
+ break;
+ case VK_NEXT:
+ nParam = VK_NUMPAD3;
+ break;
+ case VK_LEFT:
+ nParam = VK_NUMPAD4;
+ break;
+ case VK_CLEAR:
+ nParam = VK_NUMPAD5;
+ break;
+ case VK_RIGHT:
+ nParam = VK_NUMPAD6;
+ break;
+ case VK_HOME:
+ nParam = VK_NUMPAD7;
+ break;
+ case VK_UP:
+ nParam = VK_NUMPAD8;
+ break;
+ case VK_PRIOR:
+ nParam = VK_NUMPAD9;
+ break;
+ case VK_DELETE:
+ nParam = VK_DECIMAL;
+ break;
+ }
+ if (nParam) {
+ if (keystate[VK_NUMLOCK] & 1)
+ shift_state |= 1;
+ wParam = nParam;
+ }
+ }
+ }
+
+ /* If a key is pressed and AltGr is not active */
+ if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) {
+ /* Okay, prepare for most alts then ... */
+ if (left_alt)
+ *p++ = '\033';
+
+ /* Lets see if it's a pattern we know all about ... */
+ if (wParam == VK_PRIOR && shift_state == 1) {
+ SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
+ return 0;
+ }
+ if (wParam == VK_NEXT && shift_state == 1) {
+ SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
+ return 0;
+ }
+ if (wParam == VK_INSERT && shift_state == 1) {
+ term_do_paste(term);
+ return 0;
+ }
+ if (left_alt && wParam == VK_F4 && cfg.alt_f4) {
+ return -1;
+ }
+ if (left_alt && wParam == VK_SPACE && cfg.alt_space) {
+ SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0);
+ return -1;
+ }
+ if (left_alt && wParam == VK_RETURN && cfg.fullscreenonaltenter &&
+ (cfg.resize_action != RESIZE_DISABLED)) {
+ if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) != KF_REPEAT)
+ flip_full_screen();
+ return -1;
+ }
+ /* Control-Numlock for app-keypad mode switch */
+ if (wParam == VK_PAUSE && shift_state == 2) {
+ term->app_keypad_keys ^= 1;
+ return 0;
+ }
+
+ /* Nethack keypad */
+ if (cfg.nethack_keypad && !left_alt) {
+ switch (wParam) {
+ case VK_NUMPAD1:
+ *p++ = shift_state ? 'B' : 'b';
+ return p - output;
+ case VK_NUMPAD2:
+ *p++ = shift_state ? 'J' : 'j';
+ return p - output;
+ case VK_NUMPAD3:
+ *p++ = shift_state ? 'N' : 'n';
+ return p - output;
+ case VK_NUMPAD4:
+ *p++ = shift_state ? 'H' : 'h';
+ return p - output;
+ case VK_NUMPAD5:
+ *p++ = shift_state ? '.' : '.';
+ return p - output;
+ case VK_NUMPAD6:
+ *p++ = shift_state ? 'L' : 'l';
+ return p - output;
+ case VK_NUMPAD7:
+ *p++ = shift_state ? 'Y' : 'y';
+ return p - output;
+ case VK_NUMPAD8:
+ *p++ = shift_state ? 'K' : 'k';
+ return p - output;
+ case VK_NUMPAD9:
+ *p++ = shift_state ? 'U' : 'u';
+ return p - output;
+ }
+ }
+
+ /* Application Keypad */
+ if (!left_alt) {
+ int xkey = 0;
+
+ if (cfg.funky_type == 3 ||
+ (cfg.funky_type <= 1 &&
+ term->app_keypad_keys && !cfg.no_applic_k)) switch (wParam) {
+ case VK_EXECUTE:
+ xkey = 'P';
+ break;
+ case VK_DIVIDE:
+ xkey = 'Q';
+ break;
+ case VK_MULTIPLY:
+ xkey = 'R';
+ break;
+ case VK_SUBTRACT:
+ xkey = 'S';
+ break;
+ }
+ if (term->app_keypad_keys && !cfg.no_applic_k)
+ switch (wParam) {
+ case VK_NUMPAD0:
+ xkey = 'p';
+ break;
+ case VK_NUMPAD1:
+ xkey = 'q';
+ break;
+ case VK_NUMPAD2:
+ xkey = 'r';
+ break;
+ case VK_NUMPAD3:
+ xkey = 's';
+ break;
+ case VK_NUMPAD4:
+ xkey = 't';
+ break;
+ case VK_NUMPAD5:
+ xkey = 'u';
+ break;
+ case VK_NUMPAD6:
+ xkey = 'v';
+ break;
+ case VK_NUMPAD7:
+ xkey = 'w';
+ break;
+ case VK_NUMPAD8:
+ xkey = 'x';
+ break;
+ case VK_NUMPAD9:
+ xkey = 'y';
+ break;
+
+ case VK_DECIMAL:
+ xkey = 'n';
+ break;
+ case VK_ADD:
+ if (cfg.funky_type == 2) {
+ if (shift_state)
+ xkey = 'l';
+ else
+ xkey = 'k';
+ } else if (shift_state)
+ xkey = 'm';
+ else
+ xkey = 'l';
+ break;
+
+ case VK_DIVIDE:
+ if (cfg.funky_type == 2)
+ xkey = 'o';
+ break;
+ case VK_MULTIPLY:
+ if (cfg.funky_type == 2)
+ xkey = 'j';
+ break;
+ case VK_SUBTRACT:
+ if (cfg.funky_type == 2)
+ xkey = 'm';
+ break;
+
+ case VK_RETURN:
+ if (HIWORD(lParam) & KF_EXTENDED)
+ xkey = 'M';
+ break;
+ }
+ if (xkey) {
+ if (term->vt52_mode) {
+ if (xkey >= 'P' && xkey <= 'S')
+ p += sprintf((char *) p, "\x1B%c", xkey);
+ else
+ p += sprintf((char *) p, "\x1B?%c", xkey);
+ } else
+ p += sprintf((char *) p, "\x1BO%c", xkey);
+ return p - output;
+ }
+ }
+
+ if (wParam == VK_BACK && shift_state == 0) { /* Backspace */
+ *p++ = (cfg.bksp_is_delete ? 0x7F : 0x08);
+ *p++ = 0;
+ return -2;
+ }
+ if (wParam == VK_BACK && shift_state == 1) { /* Shift Backspace */
+ /* We do the opposite of what is configured */
+ *p++ = (cfg.bksp_is_delete ? 0x08 : 0x7F);
+ *p++ = 0;
+ return -2;
+ }
+ if (wParam == VK_TAB && shift_state == 1) { /* Shift tab */
+ *p++ = 0x1B;
+ *p++ = '[';
+ *p++ = 'Z';
+ return p - output;
+ }
+ if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */
+ *p++ = 0;
+ return p - output;
+ }
+ if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */
+ *p++ = 160;
+ return p - output;
+ }
+ if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */
+ *p++ = 3;
+ *p++ = 0;
+ return -2;
+ }
+ if (wParam == VK_PAUSE) { /* Break/Pause */
+ *p++ = 26;
+ *p++ = 0;
+ return -2;
+ }
+ /* Control-2 to Control-8 are special */
+ if (shift_state == 2 && wParam >= '2' && wParam <= '8') {
+ *p++ = "\000\033\034\035\036\037\177"[wParam - '2'];
+ return p - output;
+ }
+ if (shift_state == 2 && wParam == 0xBD) {
+ *p++ = 0x1F;
+ return p - output;
+ }
+ if (shift_state == 2 && wParam == 0xDF) {
+ *p++ = 0x1C;
+ return p - output;
+ }
+ if (shift_state == 0 && wParam == VK_RETURN && term->cr_lf_return) {
+ *p++ = '\r';
+ *p++ = '\n';
+ return p - output;
+ }
+
+ /*
+ * Next, all the keys that do tilde codes. (ESC '[' nn '~',
+ * for integer decimal nn.)
+ *
+ * We also deal with the weird ones here. Linux VCs replace F1
+ * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but
+ * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w
+ * respectively.
+ */
+ code = 0;
+ switch (wParam) {
+ case VK_F1:
+ code = (keystate[VK_SHIFT] & 0x80 ? 23 : 11);
+ break;
+ case VK_F2:
+ code = (keystate[VK_SHIFT] & 0x80 ? 24 : 12);
+ break;
+ case VK_F3:
+ code = (keystate[VK_SHIFT] & 0x80 ? 25 : 13);
+ break;
+ case VK_F4:
+ code = (keystate[VK_SHIFT] & 0x80 ? 26 : 14);
+ break;
+ case VK_F5:
+ code = (keystate[VK_SHIFT] & 0x80 ? 28 : 15);
+ break;
+ case VK_F6:
+ code = (keystate[VK_SHIFT] & 0x80 ? 29 : 17);
+ break;
+ case VK_F7:
+ code = (keystate[VK_SHIFT] & 0x80 ? 31 : 18);
+ break;
+ case VK_F8:
+ code = (keystate[VK_SHIFT] & 0x80 ? 32 : 19);
+ break;
+ case VK_F9:
+ code = (keystate[VK_SHIFT] & 0x80 ? 33 : 20);
+ break;
+ case VK_F10:
+ code = (keystate[VK_SHIFT] & 0x80 ? 34 : 21);
+ break;
+ case VK_F11:
+ code = 23;
+ break;
+ case VK_F12:
+ code = 24;
+ break;
+ case VK_F13:
+ code = 25;
+ break;
+ case VK_F14:
+ code = 26;
+ break;
+ case VK_F15:
+ code = 28;
+ break;
+ case VK_F16:
+ code = 29;
+ break;
+ case VK_F17:
+ code = 31;
+ break;
+ case VK_F18:
+ code = 32;
+ break;
+ case VK_F19:
+ code = 33;
+ break;
+ case VK_F20:
+ code = 34;
+ break;
+ }
+ if ((shift_state&2) == 0) switch (wParam) {
+ case VK_HOME:
+ code = 1;
+ break;
+ case VK_INSERT:
+ code = 2;
+ break;
+ case VK_DELETE:
+ code = 3;
+ break;
+ case VK_END:
+ code = 4;
+ break;
+ case VK_PRIOR:
+ code = 5;
+ break;
+ case VK_NEXT:
+ code = 6;
+ break;
+ }
+ /* Reorder edit keys to physical order */
+ if (cfg.funky_type == 3 && code <= 6)
+ code = "\0\2\1\4\5\3\6"[code];
+
+ if (term->vt52_mode && code > 0 && code <= 6) {
+ p += sprintf((char *) p, "\x1B%c", " HLMEIG"[code]);
+ return p - output;
+ }
+
+ if (cfg.funky_type == 5 && /* SCO function keys */
+ code >= 11 && code <= 34) {
+ char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
+ int index = 0;
+ switch (wParam) {
+ case VK_F1: index = 0; break;
+ case VK_F2: index = 1; break;
+ case VK_F3: index = 2; break;
+ case VK_F4: index = 3; break;
+ case VK_F5: index = 4; break;
+ case VK_F6: index = 5; break;
+ case VK_F7: index = 6; break;
+ case VK_F8: index = 7; break;
+ case VK_F9: index = 8; break;
+ case VK_F10: index = 9; break;
+ case VK_F11: index = 10; break;
+ case VK_F12: index = 11; break;
+ }
+ if (keystate[VK_SHIFT] & 0x80) index += 12;
+ if (keystate[VK_CONTROL] & 0x80) index += 24;
+ p += sprintf((char *) p, "\x1B[%c", codes[index]);
+ return p - output;
+ }
+ if (cfg.funky_type == 5 && /* SCO small keypad */
+ code >= 1 && code <= 6) {
+ char codes[] = "HL.FIG";
+ if (code == 3) {
+ *p++ = '\x7F';
+ } else {
+ p += sprintf((char *) p, "\x1B[%c", codes[code-1]);
+ }
+ return p - output;
+ }
+ if ((term->vt52_mode || cfg.funky_type == 4) && code >= 11 && code <= 24) {
+ int offt = 0;
+ if (code > 15)
+ offt++;
+ if (code > 21)
+ offt++;
+ if (term->vt52_mode)
+ p += sprintf((char *) p, "\x1B%c", code + 'P' - 11 - offt);
+ else
+ p +=
+ sprintf((char *) p, "\x1BO%c", code + 'P' - 11 - offt);
+ return p - output;
+ }
+ if (cfg.funky_type == 1 && code >= 11 && code <= 15) {
+ p += sprintf((char *) p, "\x1B[[%c", code + 'A' - 11);
+ return p - output;
+ }
+ if (cfg.funky_type == 2 && code >= 11 && code <= 14) {
+ if (term->vt52_mode)
+ p += sprintf((char *) p, "\x1B%c", code + 'P' - 11);
+ else
+ p += sprintf((char *) p, "\x1BO%c", code + 'P' - 11);
+ return p - output;
+ }
+ if (cfg.rxvt_homeend && (code == 1 || code == 4)) {
+ p += sprintf((char *) p, code == 1 ? "\x1B[H" : "\x1BOw");
+ return p - output;
+ }
+ if (code) {
+ p += sprintf((char *) p, "\x1B[%d~", code);
+ return p - output;
+ }
+
+ /*
+ * Now the remaining keys (arrows and Keypad 5. Keypad 5 for
+ * some reason seems to send VK_CLEAR to Windows...).
+ */
+ {
+ char xkey = 0;
+ switch (wParam) {
+ case VK_UP:
+ xkey = 'A';
+ break;
+ case VK_DOWN:
+ xkey = 'B';
+ break;
+ case VK_RIGHT:
+ xkey = 'C';
+ break;
+ case VK_LEFT:
+ xkey = 'D';
+ break;
+ case VK_CLEAR:
+ xkey = 'G';
+ break;
+ }
+ if (xkey) {
+ if (term->vt52_mode)
+ p += sprintf((char *) p, "\x1B%c", xkey);
+ else {
+ int app_flg = (term->app_cursor_keys && !cfg.no_applic_c);
+#if 0
+ /*
+ * RDB: VT100 & VT102 manuals both state the
+ * app cursor keys only work if the app keypad
+ * is on.
+ *
+ * SGT: That may well be true, but xterm
+ * disagrees and so does at least one
+ * application, so I've #if'ed this out and the
+ * behaviour is back to PuTTY's original: app
+ * cursor and app keypad are independently
+ * switchable modes. If anyone complains about
+ * _this_ I'll have to put in a configurable
+ * option.
+ */
+ if (!term->app_keypad_keys)
+ app_flg = 0;
+#endif
+ /* Useful mapping of Ctrl-arrows */
+ if (shift_state == 2)
+ app_flg = !app_flg;