+ {
+ Config prev_cfg;
+ int init_lvl = 1;
+
+ GetWindowText(hwnd, cfg.wintitle, sizeof(cfg.wintitle));
+ prev_cfg = cfg;
+
+ if (!do_reconfig(hwnd))
+ break;
+
+ {
+ /* Disable full-screen if resizing forbidden */
+ HMENU m = GetSystemMenu (hwnd, FALSE);
+ EnableMenuItem(m, IDM_FULLSCREEN, MF_BYCOMMAND |
+ (cfg.resize_action == RESIZE_DISABLED)
+ ? MF_GRAYED : MF_ENABLED);
+ /* Gracefully unzoom if necessary */
+ if (full_screen &&
+ (cfg.resize_action == RESIZE_DISABLED)) {
+ flip_full_screen();
+ }
+ }
+
+ if (strcmp(prev_cfg.logfilename, cfg.logfilename) ||
+ prev_cfg.logtype != cfg.logtype) {
+ logfclose(); /* reset logging */
+ logfopen();
+ }
+
+ sfree(logpal);
+ /*
+ * Flush the line discipline's edit buffer in the
+ * case where local editing has just been disabled.
+ */
+ ldisc_send(NULL, 0, 0);
+ if (pal)
+ DeleteObject(pal);
+ logpal = NULL;
+ pal = NULL;
+ cfgtopalette();
+ init_palette();
+
+ /* Screen size changed ? */
+ if (cfg.height != prev_cfg.height ||
+ cfg.width != prev_cfg.width ||
+ cfg.savelines != prev_cfg.savelines ||
+ cfg.resize_action == RESIZE_FONT ||
+ cfg.resize_action == RESIZE_DISABLED)
+ term_size(cfg.height, cfg.width, cfg.savelines);
+
+ /* Enable or disable the scroll bar, etc */
+ {
+ LONG nflg, flag = GetWindowLong(hwnd, GWL_STYLE);
+ LONG nexflag, exflag =
+ GetWindowLong(hwnd, GWL_EXSTYLE);
+
+ nexflag = exflag;
+ if (cfg.alwaysontop != prev_cfg.alwaysontop) {
+ if (cfg.alwaysontop) {
+ nexflag |= WS_EX_TOPMOST;
+ SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE);
+ } else {
+ nexflag &= ~(WS_EX_TOPMOST);
+ SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE);
+ }
+ }
+ if (cfg.sunken_edge)
+ nexflag |= WS_EX_CLIENTEDGE;
+ else
+ nexflag &= ~(WS_EX_CLIENTEDGE);
+
+ nflg = flag;
+ if (full_screen ?
+ cfg.scrollbar_in_fullscreen : cfg.scrollbar)
+ nflg |= WS_VSCROLL;
+ else
+ nflg &= ~WS_VSCROLL;
+ if (cfg.resize_action == RESIZE_DISABLED)
+ nflg &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
+ else
+ nflg |= (WS_THICKFRAME | WS_MAXIMIZEBOX);
+
+ if (nflg != flag || nexflag != exflag) {
+ if (nflg != flag)
+ SetWindowLong(hwnd, GWL_STYLE, nflg);
+ if (nexflag != exflag)
+ SetWindowLong(hwnd, GWL_EXSTYLE, nexflag);
+
+ SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
+ SWP_NOACTIVATE | SWP_NOCOPYBITS |
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
+ SWP_FRAMECHANGED);
+
+ init_lvl = 2;
+ }
+ }
+
+ /* Oops */
+ if (cfg.resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) {
+ force_normal(hwnd);
+ init_lvl = 2;
+ }
+
+ set_title(cfg.wintitle);
+ if (IsIconic(hwnd)) {
+ SetWindowText(hwnd,
+ cfg.win_name_always ? window_name :
+ icon_name);
+ }
+
+ if (strcmp(cfg.font, prev_cfg.font) != 0 ||
+ strcmp(cfg.line_codepage, prev_cfg.line_codepage) != 0 ||
+ cfg.fontisbold != prev_cfg.fontisbold ||
+ cfg.fontheight != prev_cfg.fontheight ||
+ cfg.fontcharset != prev_cfg.fontcharset ||
+ cfg.vtmode != prev_cfg.vtmode ||
+ cfg.bold_colour != prev_cfg.bold_colour ||
+ cfg.resize_action == RESIZE_DISABLED ||
+ cfg.resize_action == RESIZE_EITHER ||
+ (cfg.resize_action != prev_cfg.resize_action))
+ init_lvl = 2;
+
+ InvalidateRect(hwnd, NULL, TRUE);
+ reset_window(init_lvl);
+ net_pending_errors();
+ }
+ break;
+ case IDM_COPYALL:
+ term_copyall();
+ break;
+ case IDM_CLRSB:
+ term_clrsb();
+ break;
+ case IDM_RESET:
+ term_pwron();
+ break;
+ case IDM_TEL_AYT:
+ back->special(TS_AYT);
+ net_pending_errors();
+ break;
+ case IDM_TEL_BRK:
+ back->special(TS_BRK);
+ net_pending_errors();
+ break;
+ case IDM_TEL_SYNCH:
+ back->special(TS_SYNCH);
+ net_pending_errors();
+ break;
+ case IDM_TEL_EC:
+ back->special(TS_EC);
+ net_pending_errors();
+ break;
+ case IDM_TEL_EL:
+ back->special(TS_EL);
+ net_pending_errors();
+ break;
+ case IDM_TEL_GA:
+ back->special(TS_GA);
+ net_pending_errors();
+ break;
+ case IDM_TEL_NOP:
+ back->special(TS_NOP);
+ net_pending_errors();
+ break;
+ case IDM_TEL_ABORT:
+ back->special(TS_ABORT);
+ net_pending_errors();
+ break;
+ case IDM_TEL_AO:
+ back->special(TS_AO);
+ net_pending_errors();
+ break;
+ case IDM_TEL_IP:
+ back->special(TS_IP);
+ net_pending_errors();
+ break;
+ case IDM_TEL_SUSP:
+ back->special(TS_SUSP);
+ net_pending_errors();
+ break;
+ case IDM_TEL_EOR:
+ back->special(TS_EOR);
+ net_pending_errors();
+ break;
+ case IDM_TEL_EOF:
+ back->special(TS_EOF);
+ net_pending_errors();
+ break;
+ case IDM_ABOUT:
+ showabout(hwnd);
+ break;
+ case SC_MOUSEMENU:
+ /*
+ * We get this if the System menu has been activated
+ * using the mouse.
+ */
+ show_mouseptr(1);
+ break;
+ case SC_KEYMENU:
+ /*
+ * We get this if the System menu has been activated
+ * using the keyboard. This might happen from within
+ * TranslateKey, in which case it really wants to be
+ * followed by a `space' character to actually _bring
+ * the menu up_ rather than just sitting there in
+ * `ready to appear' state.
+ */
+ show_mouseptr(1); /* make sure pointer is visible */
+ if( lParam == 0 )
+ PostMessage(hwnd, WM_CHAR, ' ', 0);
+ break;
+ case IDM_FULLSCREEN:
+ flip_full_screen();
+ break;
+ default:
+ if (wParam >= IDM_SAVED_MIN && wParam <= IDM_SAVED_MAX) {
+ SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam);
+ }
+ }
+ break;
+
+#define X_POS(l) ((int)(short)LOWORD(l))
+#define Y_POS(l) ((int)(short)HIWORD(l))
+
+#define TO_CHR_X(x) ((((x)<0 ? (x)-font_width+1 : (x))-offset_width) / font_width)
+#define TO_CHR_Y(y) ((((y)<0 ? (y)-font_height+1: (y))-offset_height) / font_height)
+#define WHEEL_DELTA 120
+ case WM_MOUSEWHEEL:
+ {
+ wheel_accumulator += (short) HIWORD(wParam);
+ wParam = LOWORD(wParam);
+
+ /* process events when the threshold is reached */
+ while (abs(wheel_accumulator) >= WHEEL_DELTA) {
+ int b;
+
+ /* reduce amount for next time */
+ if (wheel_accumulator > 0) {
+ b = MBT_WHEEL_UP;
+ wheel_accumulator -= WHEEL_DELTA;
+ } else if (wheel_accumulator < 0) {
+ b = MBT_WHEEL_DOWN;
+ wheel_accumulator += WHEEL_DELTA;
+ } else
+ break;
+
+ if (send_raw_mouse) {
+ /* send a mouse-down followed by a mouse up */
+
+ term_mouse(b,
+ MA_CLICK,
+ TO_CHR_X(X_POS(lParam)),
+ TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
+ wParam & MK_CONTROL, is_alt_pressed());
+ term_mouse(b, MA_RELEASE, TO_CHR_X(X_POS(lParam)),
+ TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
+ wParam & MK_CONTROL, is_alt_pressed());
+ } else {
+ /* trigger a scroll */
+ term_scroll(0,
+ b == MBT_WHEEL_UP ? -rows / 2 : rows / 2);
+ }
+ }
+ return 0;
+ }
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ {
+ int button, press;
+
+ switch (message) {
+ case WM_LBUTTONDOWN:
+ button = MBT_LEFT;
+ press = 1;
+ break;
+ case WM_MBUTTONDOWN:
+ button = MBT_MIDDLE;
+ press = 1;
+ break;
+ case WM_RBUTTONDOWN:
+ button = MBT_RIGHT;
+ press = 1;
+ break;
+ case WM_LBUTTONUP:
+ button = MBT_LEFT;
+ press = 0;
+ break;
+ case WM_MBUTTONUP:
+ button = MBT_MIDDLE;
+ press = 0;
+ break;
+ case WM_RBUTTONUP:
+ button = MBT_RIGHT;
+ press = 0;
+ break;
+ default:
+ button = press = 0; /* shouldn't happen */
+ }
+ show_mouseptr(1);
+ /*
+ * Special case: in full-screen mode, if the left
+ * button is clicked in the very top left corner of the
+ * window, we put up the System menu instead of doing
+ * selection.
+ */
+ if (full_screen && press && button == MBT_LEFT &&
+ X_POS(lParam) == 0 && Y_POS(lParam) == 0) {
+ SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, 0);
+ return 0;
+ }
+ if (press) {
+ click(button,
+ TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)),
+ wParam & MK_SHIFT, wParam & MK_CONTROL,
+ is_alt_pressed());
+ SetCapture(hwnd);
+ } else {
+ term_mouse(button, MA_RELEASE,
+ TO_CHR_X(X_POS(lParam)),
+ TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
+ wParam & MK_CONTROL, is_alt_pressed());
+ ReleaseCapture();
+ }
+ }
+ return 0;
+ case WM_MOUSEMOVE:
+ show_mouseptr(1);
+ /*
+ * Add the mouse position and message time to the random
+ * number noise.
+ */
+ noise_ultralight(lParam);
+
+ if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) {
+ Mouse_Button b;
+ if (wParam & MK_LBUTTON)
+ b = MBT_LEFT;
+ else if (wParam & MK_MBUTTON)
+ b = MBT_MIDDLE;
+ else
+ b = MBT_RIGHT;
+ term_mouse(b, MA_DRAG, TO_CHR_X(X_POS(lParam)),
+ TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT,
+ wParam & MK_CONTROL, is_alt_pressed());
+ }
+ return 0;
+ case WM_NCMOUSEMOVE:
+ show_mouseptr(1);
+ noise_ultralight(lParam);
+ return 0;
+ case WM_IGNORE_CLIP:
+ ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */
+ break;
+ case WM_DESTROYCLIPBOARD:
+ if (!ignore_clip)
+ term_deselect();
+ ignore_clip = FALSE;
+ return 0;
+ case WM_PAINT:
+ {
+ PAINTSTRUCT p;
+ HideCaret(hwnd);
+ hdc = BeginPaint(hwnd, &p);
+ if (pal) {
+ SelectPalette(hdc, pal, TRUE);
+ RealizePalette(hdc);
+ }
+ term_paint(hdc,
+ (p.rcPaint.left-offset_width)/font_width,
+ (p.rcPaint.top-offset_height)/font_height,
+ (p.rcPaint.right-offset_width-1)/font_width,
+ (p.rcPaint.bottom-offset_height-1)/font_height);
+
+ if (p.fErase ||
+ p.rcPaint.left < offset_width ||
+ p.rcPaint.top < offset_height ||
+ p.rcPaint.right >= offset_width + font_width*cols ||
+ p.rcPaint.bottom>= offset_height + font_height*rows)
+ {
+ HBRUSH fillcolour, oldbrush;
+ HPEN edge, oldpen;
+ fillcolour = CreateSolidBrush (
+ colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
+ oldbrush = SelectObject(hdc, fillcolour);
+ edge = CreatePen(PS_SOLID, 0,
+ colours[(ATTR_DEFBG>>ATTR_BGSHIFT)*2]);
+ oldpen = SelectObject(hdc, edge);
+
+ ExcludeClipRect(hdc,
+ offset_width, offset_height,
+ offset_width+font_width*cols,
+ offset_height+font_height*rows);
+
+ Rectangle(hdc, p.rcPaint.left, p.rcPaint.top,
+ p.rcPaint.right, p.rcPaint.bottom);
+
+ // SelectClipRgn(hdc, NULL);
+
+ SelectObject(hdc, oldbrush);
+ DeleteObject(fillcolour);
+ SelectObject(hdc, oldpen);
+ DeleteObject(edge);
+ }
+ SelectObject(hdc, GetStockObject(SYSTEM_FONT));
+ SelectObject(hdc, GetStockObject(WHITE_PEN));
+ EndPaint(hwnd, &p);
+ ShowCaret(hwnd);
+ }
+ return 0;
+ case WM_NETEVENT:
+ /* Notice we can get multiple netevents, FD_READ, FD_WRITE etc
+ * but the only one that's likely to try to overload us is FD_READ.
+ * This means buffering just one is fine.
+ */
+ if (pending_netevent)
+ enact_pending_netevent();
+
+ pending_netevent = TRUE;
+ pend_netevent_wParam = wParam;
+ pend_netevent_lParam = lParam;
+ if (WSAGETSELECTEVENT(lParam) != FD_READ)
+ enact_pending_netevent();
+
+ time(&last_movement);
+ return 0;
+ case WM_SETFOCUS:
+ has_focus = TRUE;
+ CreateCaret(hwnd, caretbm, font_width, font_height);
+ ShowCaret(hwnd);
+ flash_window(0); /* stop */
+ compose_state = 0;
+ term_out();
+ term_update();
+ break;
+ case WM_KILLFOCUS:
+ if (full_screen) flip_full_screen();
+ show_mouseptr(1);
+ has_focus = FALSE;
+ DestroyCaret();
+ term_out();
+ term_update();
+ break;
+ case WM_ENTERSIZEMOVE:
+#ifdef RDB_DEBUG_PATCH
+ debug((27, "WM_ENTERSIZEMOVE"));
+#endif
+ EnableSizeTip(1);
+ resizing = TRUE;
+ need_backend_resize = FALSE;
+ break;
+ case WM_EXITSIZEMOVE:
+ EnableSizeTip(0);
+ resizing = FALSE;
+#ifdef RDB_DEBUG_PATCH
+ debug((27, "WM_EXITSIZEMOVE"));
+#endif
+ if (need_backend_resize) {
+ term_size(cfg.height, cfg.width, cfg.savelines);
+ InvalidateRect(hwnd, NULL, TRUE);
+ }
+ break;
+ case WM_SIZING:
+ /*
+ * This does two jobs:
+ * 1) Keep the sizetip uptodate
+ * 2) Make sure the window size is _stepped_ in units of the font size.
+ */
+ if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
+ int width, height, w, h, ew, eh;
+ LPRECT r = (LPRECT) lParam;
+
+ if ( !need_backend_resize && cfg.resize_action == RESIZE_EITHER &&
+ (cfg.height != rows || cfg.width != cols )) {
+ /*
+ * Great! It seems that both the terminal size and the
+ * font size have been changed and the user is now dragging.
+ *
+ * It will now be difficult to get back to the configured
+ * font size!
+ *
+ * This would be easier but it seems to be too confusing.
+
+ term_size(cfg.height, cfg.width, cfg.savelines);
+ reset_window(2);
+ */
+ cfg.height=rows; cfg.width=cols;
+
+ InvalidateRect(hwnd, NULL, TRUE);
+ need_backend_resize = TRUE;
+ }
+
+ width = r->right - r->left - extra_width;
+ height = r->bottom - r->top - extra_height;
+ w = (width + font_width / 2) / font_width;
+ if (w < 1)
+ w = 1;
+ h = (height + font_height / 2) / font_height;
+ if (h < 1)
+ h = 1;
+ UpdateSizeTip(hwnd, w, h);
+ ew = width - w * font_width;
+ eh = height - h * font_height;
+ if (ew != 0) {
+ if (wParam == WMSZ_LEFT ||
+ wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
+ r->left += ew;
+ else
+ r->right -= ew;
+ }
+ if (eh != 0) {
+ if (wParam == WMSZ_TOP ||
+ wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
+ r->top += eh;
+ else
+ r->bottom -= eh;
+ }
+ if (ew || eh)
+ return 1;
+ else
+ return 0;
+ } else {
+ int width, height, w, h, rv = 0;
+ int ex_width = extra_width + (cfg.window_border - offset_width) * 2;
+ int ex_height = extra_height + (cfg.window_border - offset_height) * 2;
+ LPRECT r = (LPRECT) lParam;
+
+ width = r->right - r->left - ex_width;
+ height = r->bottom - r->top - ex_height;
+
+ w = (width + cols/2)/cols;
+ h = (height + rows/2)/rows;
+ if ( r->right != r->left + w*cols + ex_width)
+ rv = 1;
+
+ if (wParam == WMSZ_LEFT ||
+ wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT)
+ r->left = r->right - w*cols - ex_width;
+ else
+ r->right = r->left + w*cols + ex_width;
+
+ if (r->bottom != r->top + h*rows + ex_height)
+ rv = 1;
+
+ if (wParam == WMSZ_TOP ||
+ wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
+ r->top = r->bottom - h*rows - ex_height;
+ else
+ r->bottom = r->top + h*rows + ex_height;
+
+ return rv;
+ }
+ /* break; (never reached) */
+ case WM_SIZE:
+#ifdef RDB_DEBUG_PATCH
+ debug((27, "WM_SIZE %s (%d,%d)",
+ (wParam == SIZE_MINIMIZED) ? "SIZE_MINIMIZED":
+ (wParam == SIZE_MAXIMIZED) ? "SIZE_MAXIMIZED":
+ (wParam == SIZE_RESTORED && resizing) ? "to":
+ (wParam == SIZE_RESTORED) ? "SIZE_RESTORED":
+ "...",
+ LOWORD(lParam), HIWORD(lParam)));
+#endif
+ if (wParam == SIZE_MINIMIZED)
+ SetWindowText(hwnd,
+ cfg.win_name_always ? window_name : icon_name);
+ if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
+ SetWindowText(hwnd, window_name);
+
+ if (cfg.resize_action == RESIZE_DISABLED) {
+ /* A resize, well it better be a minimize. */
+ reset_window(-1);
+ } else {
+
+ int width, height, w, h;
+
+ width = LOWORD(lParam);
+ height = HIWORD(lParam);
+
+ if (!resizing) {
+ if (wParam == SIZE_MAXIMIZED && !was_zoomed) {
+ was_zoomed = 1;
+ prev_rows = rows;
+ prev_cols = cols;
+ if (cfg.resize_action == RESIZE_TERM) {
+ w = width / font_width;
+ if (w < 1) w = 1;
+ h = height / font_height;
+ if (h < 1) h = 1;
+
+ term_size(h, w, cfg.savelines);
+ }
+ reset_window(0);
+ } else if (wParam == SIZE_RESTORED && was_zoomed) {
+ was_zoomed = 0;
+ if (cfg.resize_action == RESIZE_TERM)
+ term_size(prev_rows, prev_cols, cfg.savelines);
+ if (cfg.resize_action != RESIZE_FONT)
+ reset_window(2);
+ else
+ reset_window(0);
+ }
+ /* This is an unexpected resize, these will normally happen
+ * if the window is too large. Probably either the user
+ * selected a huge font or the screen size has changed.
+ *
+ * This is also called with minimize.
+ */
+ else reset_window(-1);
+ }
+
+ /*
+ * Don't call back->size in mid-resize. (To prevent
+ * massive numbers of resize events getting sent
+ * down the connection during an NT opaque drag.)
+ */
+ if (resizing) {
+ if (cfg.resize_action != RESIZE_FONT && !alt_pressed) {
+ need_backend_resize = TRUE;
+ w = (width-cfg.window_border*2) / font_width;
+ if (w < 1) w = 1;
+ h = (height-cfg.window_border*2) / font_height;
+ if (h < 1) h = 1;
+
+ cfg.height = h;
+ cfg.width = w;
+ } else
+ reset_window(0);
+ }
+ }
+ return 0;
+ case WM_VSCROLL:
+ switch (LOWORD(wParam)) {
+ case SB_BOTTOM:
+ term_scroll(-1, 0);
+ break;
+ case SB_TOP:
+ term_scroll(+1, 0);
+ break;
+ case SB_LINEDOWN:
+ term_scroll(0, +1);
+ break;
+ case SB_LINEUP:
+ term_scroll(0, -1);
+ break;
+ case SB_PAGEDOWN:
+ term_scroll(0, +rows / 2);
+ break;
+ case SB_PAGEUP:
+ term_scroll(0, -rows / 2);
+ break;
+ case SB_THUMBPOSITION:
+ case SB_THUMBTRACK:
+ term_scroll(1, HIWORD(wParam));
+ break;
+ }
+ break;
+ case WM_PALETTECHANGED:
+ if ((HWND) wParam != hwnd && pal != NULL) {
+ HDC hdc = get_ctx();
+ if (hdc) {
+ if (RealizePalette(hdc) > 0)
+ UpdateColors(hdc);
+ free_ctx(hdc);
+ }
+ }
+ break;
+ case WM_QUERYNEWPALETTE:
+ if (pal != NULL) {
+ HDC hdc = get_ctx();
+ if (hdc) {
+ if (RealizePalette(hdc) > 0)
+ UpdateColors(hdc);
+ free_ctx(hdc);
+ return TRUE;
+ }
+ }
+ return FALSE;
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ /*
+ * Add the scan code and keypress timing to the random
+ * number noise.
+ */
+ noise_ultralight(lParam);
+
+ /*
+ * We don't do TranslateMessage since it disassociates the
+ * resulting CHAR message from the KEYDOWN that sparked it,
+ * which we occasionally don't want. Instead, we process
+ * KEYDOWN, and call the Win32 translator functions so that
+ * we get the translations under _our_ control.
+ */
+ {
+ unsigned char buf[20];
+ int len;
+
+ if (wParam == VK_PROCESSKEY) {
+ MSG m;
+ m.hwnd = hwnd;
+ m.message = WM_KEYDOWN;
+ m.wParam = wParam;
+ m.lParam = lParam & 0xdfff;
+ TranslateMessage(&m);
+ } else {
+ len = TranslateKey(message, wParam, lParam, buf);
+ if (len == -1)
+ return DefWindowProc(hwnd, message, wParam, lParam);
+
+ if (len != 0) {
+ /*
+ * Interrupt an ongoing paste. I'm not sure
+ * this is sensible, but for the moment it's
+ * preferable to having to faff about buffering
+ * things.
+ */
+ term_nopaste();
+
+ /*
+ * We need not bother about stdin backlogs
+ * here, because in GUI PuTTY we can't do
+ * anything about it anyway; there's no means
+ * of asking Windows to hold off on KEYDOWN
+ * messages. We _have_ to buffer everything
+ * we're sent.
+ */
+ ldisc_send(buf, len, 1);
+ show_mouseptr(0);
+ }
+ }
+ }
+ net_pending_errors();
+ return 0;
+ case WM_INPUTLANGCHANGE:
+ /* wParam == Font number */
+ /* lParam == Locale */
+ set_input_locale((HKL)lParam);
+ break;
+ case WM_IME_NOTIFY:
+ if(wParam == IMN_SETOPENSTATUS) {
+ HIMC hImc = ImmGetContext(hwnd);
+ ImmSetCompositionFont(hImc, &lfont);
+ ImmReleaseContext(hwnd, hImc);
+ return 0;
+ }
+ break;
+ case WM_IME_COMPOSITION:
+ {
+ HIMC hIMC;
+ int n;
+ char *buff;
+
+ if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ||
+ osVersion.dwPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */
+
+ if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */
+ break; /* fall back to DefWindowProc */
+
+ hIMC = ImmGetContext(hwnd);
+ n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
+
+ if (n > 0) {
+ buff = (char*) smalloc(n);
+ ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
+ luni_send((unsigned short *)buff, n / 2, 1);
+ free(buff);
+ }
+ ImmReleaseContext(hwnd, hIMC);
+ return 1;
+ }
+
+ case WM_IME_CHAR:
+ if (wParam & 0xFF00) {
+ unsigned char buf[2];
+
+ buf[1] = wParam;
+ buf[0] = wParam >> 8;
+ lpage_send(kbd_codepage, buf, 2, 1);
+ } else {
+ char c = (unsigned char) wParam;
+ lpage_send(kbd_codepage, &c, 1, 1);
+ }
+ return (0);
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ /*
+ * Nevertheless, we are prepared to deal with WM_CHAR
+ * messages, should they crop up. So if someone wants to
+ * post the things to us as part of a macro manoeuvre,
+ * we're ready to cope.
+ */
+ {
+ char c = (unsigned char)wParam;
+ lpage_send(CP_ACP, &c, 1, 1);
+ }
+ return 0;
+ case WM_SETCURSOR:
+ if (send_raw_mouse && LOWORD(lParam) == HTCLIENT) {
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+ return TRUE;
+ }
+ }
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+/*
+ * Move the system caret. (We maintain one, even though it's
+ * invisible, for the benefit of blind people: apparently some
+ * helper software tracks the system caret, so we should arrange to
+ * have one.)
+ */
+void sys_cursor(int x, int y)
+{
+ COMPOSITIONFORM cf;
+ HIMC hIMC;
+
+ if (!has_focus) return;
+
+ SetCaretPos(x * font_width + offset_width,
+ y * font_height + offset_height);
+
+ /* IMM calls on Win98 and beyond only */
+ if(osVersion.dwPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */
+
+ if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
+ osVersion.dwMinorVersion == 0) return; /* 95 */
+
+ /* we should have the IMM functions */
+ hIMC = ImmGetContext(hwnd);
+ cf.dwStyle = CFS_POINT;
+ cf.ptCurrentPos.x = x * font_width + offset_width;
+ cf.ptCurrentPos.y = y * font_height + offset_height;
+ ImmSetCompositionWindow(hIMC, &cf);
+
+ ImmReleaseContext(hwnd, hIMC);
+}
+
+/*
+ * Draw a line of text in the window, at given character
+ * coordinates, in given attributes.
+ *
+ * We are allowed to fiddle with the contents of `text'.
+ */
+void do_text(Context ctx, int x, int y, char *text, int len,
+ unsigned long attr, int lattr)
+{
+ COLORREF fg, bg, t;
+ int nfg, nbg, nfont;
+ HDC hdc = ctx;
+ RECT line_box;
+ int force_manual_underline = 0;
+ int fnt_width = font_width * (1 + (lattr != LATTR_NORM));
+ int char_width = fnt_width;
+ int text_adjust = 0;
+ static int *IpDx = 0, IpDxLEN = 0;
+
+ if (attr & ATTR_WIDE)
+ char_width *= 2;
+
+ if (len > IpDxLEN || IpDx[0] != char_width) {
+ int i;
+ if (len > IpDxLEN) {
+ sfree(IpDx);
+ IpDx = smalloc((len + 16) * sizeof(int));
+ IpDxLEN = (len + 16);
+ }
+ for (i = 0; i < IpDxLEN; i++)
+ IpDx[i] = char_width;
+ }
+
+ /* Only want the left half of double width lines */
+ if (lattr != LATTR_NORM && x*2 >= cols)
+ return;
+
+ x *= fnt_width;
+ y *= font_height;
+ x += offset_width;
+ y += offset_height;
+
+ if ((attr & TATTR_ACTCURS) && (cfg.cursor_type == 0 || big_cursor)) {
+ attr &= ATTR_CUR_AND | (bold_mode != BOLD_COLOURS ? ATTR_BOLD : 0);
+ attr ^= ATTR_CUR_XOR;
+ }
+
+ nfont = 0;
+ if (cfg.vtmode == VT_POORMAN && lattr != LATTR_NORM) {
+ /* Assume a poorman font is borken in other ways too. */
+ lattr = LATTR_WIDE;
+ } else
+ switch (lattr) {
+ case LATTR_NORM:
+ break;
+ case LATTR_WIDE:
+ nfont |= FONT_WIDE;
+ break;
+ default:
+ nfont |= FONT_WIDE + FONT_HIGH;
+ break;
+ }
+ if (attr & ATTR_NARROW)
+ nfont |= FONT_NARROW;
+
+ /* Special hack for the VT100 linedraw glyphs. */
+ if ((attr & CSET_MASK) == 0x2300) {
+ if (text[0] >= (char) 0xBA && text[0] <= (char) 0xBD) {
+ switch ((unsigned char) (text[0])) {
+ case 0xBA:
+ text_adjust = -2 * font_height / 5;
+ break;
+ case 0xBB:
+ text_adjust = -1 * font_height / 5;
+ break;
+ case 0xBC:
+ text_adjust = font_height / 5;
+ break;
+ case 0xBD:
+ text_adjust = 2 * font_height / 5;
+ break;
+ }
+ if (lattr == LATTR_TOP || lattr == LATTR_BOT)
+ text_adjust *= 2;
+ attr &= ~CSET_MASK;
+ text[0] = (char) (unitab_xterm['q'] & CHAR_MASK);
+ attr |= (unitab_xterm['q'] & CSET_MASK);
+ if (attr & ATTR_UNDER) {
+ attr &= ~ATTR_UNDER;
+ force_manual_underline = 1;
+ }
+ }
+ }
+
+ /* Anything left as an original character set is unprintable. */
+ if (DIRECT_CHAR(attr)) {
+ attr &= ~CSET_MASK;
+ attr |= 0xFF00;
+ memset(text, 0xFD, len);
+ }
+
+ /* OEM CP */
+ if ((attr & CSET_MASK) == ATTR_OEMCP)
+ nfont |= FONT_OEM;
+
+ nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
+ nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
+ if (bold_mode == BOLD_FONT && (attr & ATTR_BOLD))
+ nfont |= FONT_BOLD;
+ if (und_mode == UND_FONT && (attr & ATTR_UNDER))
+ nfont |= FONT_UNDERLINE;
+ another_font(nfont);
+ if (!fonts[nfont]) {
+ if (nfont & FONT_UNDERLINE)
+ force_manual_underline = 1;
+ /* Don't do the same for manual bold, it could be bad news. */
+
+ nfont &= ~(FONT_BOLD | FONT_UNDERLINE);
+ }
+ another_font(nfont);
+ if (!fonts[nfont])
+ nfont = FONT_NORMAL;
+ if (attr & ATTR_REVERSE) {
+ t = nfg;
+ nfg = nbg;
+ nbg = t;
+ }
+ if (bold_mode == BOLD_COLOURS && (attr & ATTR_BOLD))
+ nfg++;
+ if (bold_mode == BOLD_COLOURS && (attr & ATTR_BLINK))
+ nbg++;
+ fg = colours[nfg];
+ bg = colours[nbg];
+ SelectObject(hdc, fonts[nfont]);
+ SetTextColor(hdc, fg);
+ SetBkColor(hdc, bg);
+ SetBkMode(hdc, OPAQUE);
+ line_box.left = x;
+ line_box.top = y;
+ line_box.right = x + char_width * len;
+ line_box.bottom = y + font_height;
+
+ /* Only want the left half of double width lines */
+ if (line_box.right > font_width*cols+offset_width)
+ line_box.right = font_width*cols+offset_width;
+
+ /* We're using a private area for direct to font. (512 chars.) */
+ if (dbcs_screenfont && (attr & CSET_MASK) == ATTR_ACP) {
+ /* Ho Hum, dbcs fonts are a PITA! */
+ /* To display on W9x I have to convert to UCS */
+ static wchar_t *uni_buf = 0;
+ static int uni_len = 0;
+ int nlen, mptr;
+ if (len > uni_len) {
+ sfree(uni_buf);
+ uni_buf = smalloc((uni_len = len) * sizeof(wchar_t));
+ }
+
+ for(nlen = mptr = 0; mptr<len; mptr++) {
+ uni_buf[nlen] = 0xFFFD;
+ if (IsDBCSLeadByteEx(font_codepage, (BYTE) text[mptr])) {
+ IpDx[nlen] += char_width;
+ MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
+ text+mptr, 2, uni_buf+nlen, 1);
+ mptr++;
+ }
+ else
+ {
+ MultiByteToWideChar(font_codepage, MB_USEGLYPHCHARS,
+ text+mptr, 1, uni_buf+nlen, 1);
+ }
+ nlen++;
+ }
+ if (nlen <= 0)
+ return; /* Eeek! */
+
+ ExtTextOutW(hdc, x,
+ y - font_height * (lattr == LATTR_BOT) + text_adjust,
+ ETO_CLIPPED | ETO_OPAQUE, &line_box, uni_buf, nlen, IpDx);
+ if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
+ SetBkMode(hdc, TRANSPARENT);
+ ExtTextOutW(hdc, x - 1,
+ y - font_height * (lattr ==
+ LATTR_BOT) + text_adjust,
+ ETO_CLIPPED, &line_box, uni_buf, nlen, IpDx);
+ }
+
+ IpDx[0] = -1;
+ } else if (DIRECT_FONT(attr)) {
+ ExtTextOut(hdc, x,
+ y - font_height * (lattr == LATTR_BOT) + text_adjust,
+ ETO_CLIPPED | ETO_OPAQUE, &line_box, text, len, IpDx);
+ if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
+ SetBkMode(hdc, TRANSPARENT);
+
+ /* GRR: This draws the character outside it's box and can leave
+ * 'droppings' even with the clip box! I suppose I could loop it
+ * one character at a time ... yuk.
+ *
+ * Or ... I could do a test print with "W", and use +1 or -1 for this
+ * shift depending on if the leftmost column is blank...
+ */
+ ExtTextOut(hdc, x - 1,
+ y - font_height * (lattr ==
+ LATTR_BOT) + text_adjust,
+ ETO_CLIPPED, &line_box, text, len, IpDx);
+ }
+ } else {
+ /* And 'normal' unicode characters */
+ static WCHAR *wbuf = NULL;
+ static int wlen = 0;
+ int i;
+ if (wlen < len) {
+ sfree(wbuf);
+ wlen = len;
+ wbuf = smalloc(wlen * sizeof(WCHAR));
+ }
+ for (i = 0; i < len; i++)
+ wbuf[i] = (WCHAR) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
+
+ ExtTextOutW(hdc, x,
+ y - font_height * (lattr == LATTR_BOT) + text_adjust,
+ ETO_CLIPPED | ETO_OPAQUE, &line_box, wbuf, len, IpDx);
+
+ /* And the shadow bold hack. */
+ if (bold_mode == BOLD_SHADOW && (attr & ATTR_BOLD)) {
+ SetBkMode(hdc, TRANSPARENT);
+ ExtTextOutW(hdc, x - 1,
+ y - font_height * (lattr ==
+ LATTR_BOT) + text_adjust,
+ ETO_CLIPPED, &line_box, wbuf, len, IpDx);
+ }
+ }
+ if (lattr != LATTR_TOP && (force_manual_underline ||
+ (und_mode == UND_LINE
+ && (attr & ATTR_UNDER)))) {
+ HPEN oldpen;
+ int dec = descent;
+ if (lattr == LATTR_BOT)
+ dec = dec * 2 - font_height;
+
+ oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, fg));
+ MoveToEx(hdc, x, y + dec, NULL);
+ LineTo(hdc, x + len * char_width, y + dec);
+ oldpen = SelectObject(hdc, oldpen);
+ DeleteObject(oldpen);
+ }
+}
+
+void do_cursor(Context ctx, int x, int y, char *text, int len,
+ unsigned long attr, int lattr)
+{
+
+ int fnt_width;
+ int char_width;
+ HDC hdc = ctx;
+ int ctype = cfg.cursor_type;
+
+ if ((attr & TATTR_ACTCURS) && (ctype == 0 || big_cursor)) {
+ if (((attr & CSET_MASK) | (unsigned char) *text) != UCSWIDE) {
+ do_text(ctx, x, y, text, len, attr, lattr);
+ return;
+ }
+ ctype = 2;
+ attr |= TATTR_RIGHTCURS;
+ }
+
+ fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM));
+ if (attr & ATTR_WIDE)
+ char_width *= 2;
+ x *= fnt_width;
+ y *= font_height;
+ x += offset_width;
+ y += offset_height;
+
+ if ((attr & TATTR_PASCURS) && (ctype == 0 || big_cursor)) {
+ POINT pts[5];
+ HPEN oldpen;
+ pts[0].x = pts[1].x = pts[4].x = x;
+ pts[2].x = pts[3].x = x + char_width - 1;
+ pts[0].y = pts[3].y = pts[4].y = y;
+ pts[1].y = pts[2].y = y + font_height - 1;
+ oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
+ Polyline(hdc, pts, 5);
+ oldpen = SelectObject(hdc, oldpen);
+ DeleteObject(oldpen);
+ } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) {
+ int startx, starty, dx, dy, length, i;
+ if (ctype == 1) {
+ startx = x;
+ starty = y + descent;
+ dx = 1;
+ dy = 0;
+ length = char_width;
+ } else {
+ int xadjust = 0;
+ if (attr & TATTR_RIGHTCURS)
+ xadjust = char_width - 1;
+ startx = x + xadjust;
+ starty = y;
+ dx = 0;
+ dy = 1;
+ length = font_height;
+ }
+ if (attr & TATTR_ACTCURS) {
+ HPEN oldpen;
+ oldpen =
+ SelectObject(hdc, CreatePen(PS_SOLID, 0, colours[23]));
+ MoveToEx(hdc, startx, starty, NULL);
+ LineTo(hdc, startx + dx * length, starty + dy * length);
+ oldpen = SelectObject(hdc, oldpen);
+ DeleteObject(oldpen);
+ } else {
+ for (i = 0; i < length; i++) {
+ if (i % 2 == 0) {
+ SetPixel(hdc, startx, starty, colours[23]);
+ }
+ startx += dx;
+ starty += dy;
+ }
+ }
+ }
+}
+
+/* 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 && 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 (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;
+ }
+ }
+
+ alt_pressed = (left_alt && key_down);
+
+ 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;
+
+ /*
+ * Record that we pressed key so the scroll window can be reset, but
+ * be careful to avoid Shift-UP/Down
+ */
+ if (wParam != VK_SHIFT && wParam != VK_PRIOR && wParam != VK_NEXT &&
+ wParam != VK_MENU && wParam != VK_CONTROL) {
+ seen_key_event = 1;
+ }
+
+ if (compose_state > 1 && left_alt)
+ compose_state = 0;
+
+ /* Sanitize the number pad if not using a PC NumPad */
+ if (left_alt || (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;