diff -ru clean/windows/window.c unicode-input/windows/window.c
--- clean/windows/window.c	2011-02-16 21:41:57.717852500 -0500
+++ unicode-input/windows/window.c	2011-02-18 20:15:50.992173300 -0500
@@ -89,6 +89,7 @@
 static void set_input_locale(HKL);
 static void update_savedsess_menu(void);
 static void init_flashwindow(void);
+static void init_tounicode(void);
 
 static int is_full_screen(void);
 static void make_full_screen(void);
@@ -355,6 +356,8 @@
 
     init_flashwindow();
 
+    init_tounicode();
+
     /*
      * Initialize COM.
      */
@@ -3050,6 +3053,25 @@
 		lpage_send(ldisc, CP_ACP, &c, 1, 1);
 	}
 	return 0;
+      case WM_UNICHAR:
+	{
+	    wchar_t cbuf[2];
+	    int n;
+#if 0
+	    debug(("WM_UNICHAR %04x\n", wParam));
+#endif
+	    if(wParam == UNICODE_NOCHAR) return TRUE;
+	    if(wParam < 0x10000) {
+		cbuf[0] = wParam;
+		n=1;
+	    } else {
+		cbuf[0] = 0xD800 | ((wParam - 0x10000) >> 10);
+		cbuf[1] = 0xDC00 | (wParam - 0x10000) & 0x2FF;
+		n=2;
+	    }
+	    luni_send(ldisc, cbuf, n, 1);
+	}
+	return TRUE;
       case WM_SYSCOLORCHANGE:
 	if (cfg.system_colour) {
 	    /* Refresh palette from system colours. */
@@ -3675,6 +3697,8 @@
     return ibuf;
 }
 
+DECL_WINDOWS_FUNCTION(static, int, ToUnicodeEx, (UINT, UINT, const BYTE *, LPWSTR, int, UINT, HKL));
+
 /*
  * Translate a WM_(SYS)?KEY(UP|DOWN) message into a string of ASCII
  * codes. Returns number of bytes used, zero to drop the message,
@@ -3692,8 +3716,8 @@
 
     HKL kbd_layout = GetKeyboardLayout(0);
 
-    /* keys is for ToAsciiEx. There's some ick here, see below. */
-    static WORD keys[3];
+    /* keys for ToUnicodeEx. */
+    static wchar_t keys[10];
     static int compose_char = 0;
     static WPARAM compose_key = 0;
 
@@ -3721,6 +3745,8 @@
 		debug((".\n"));
 		if (wParam >= VK_F1 && wParam <= VK_F20)
 		    debug(("K_F%d", wParam + 1 - VK_F1));
+		else if (wParam >= 'A' && wParam <= 'Z' || wParam >= '0' && wParam <= '9' || wParam == ' ')
+		    debug(("K_'%c'", wParam));
 		else
 		    switch (wParam) {
 		      case VK_SHIFT:
@@ -4361,7 +4387,9 @@
 	/* XXX how do we know what the max size of the keys array should
 	 * be is? There's indication on MS' website of an Inquire/InquireEx
 	 * functioning returning a KBINFO structure which tells us. */
-	if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+	if(p_ToUnicodeEx) {
+		r = (*p_ToUnicodeEx)(wParam, scan, keystate, keys, sizeof keys/sizeof(wchar_t), 0, kbd_layout);
+	} else if (osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT) {
 	    /* XXX 'keys' parameter is declared in MSDN documentation as
 	     * 'LPWORD lpChar'.
 	     * The experience of a French user indicates that on
@@ -4372,12 +4400,52 @@
 	     * Win9x/NT split, but I suspect it's worse than that.
 	     * See wishlist item `win-dead-keys' for more horrible detail
 	     * and speculations. */
-	    BYTE keybs[3];
-	    int i;
-	    r = ToAsciiEx(wParam, scan, keystate, (LPWORD)keybs, 0, kbd_layout);
-	    for (i=0; i<3; i++) keys[i] = keybs[i];
+	    char ansikeys[10];
+	    /* Actualy on NT it's probably just a wrapper for
+	     * ToUnicodeEx that converts back via WCToMB. */
+	    r = ToAsciiEx(wParam, scan, keystate, (LPWORD)ansikeys, 0, kbd_layout);
+	    if(r > 0) {
+#ifdef SHOW_TOASCII_RESULT
+		{
+		    int r1;
+		    debug((", NT_ASC("));
+		    for (r1 = 0; r1 < r; r1++) {
+			debug(("%s%d", r1 ? ", " : "", ansikeys[r1]));
+		    }
+		    debug((")"));
+		}
+#endif
+		r = mb_to_wc(kbd_codepage,0,ansikeys,r,keys,sizeof keys/sizeof(wchar_t));
+	    }
 	} else {
-	    r = ToAsciiEx(wParam, scan, keystate, keys, 0, kbd_layout);
+	    static WORD ansikeys[3];
+	    /* Static because 9x uses [0] for temporary state for dead
+	     * keys and alt-numlock. */
+	    r = ToAsciiEx(wParam, scan, keystate, ansikeys, 0, kbd_layout);
+	    if(r > 0) {
+		int r1;
+		for(r1=0;r1<r;r1++) {
+		    char ch = (unsigned char)ansikeys[r1];
+		    mb_to_wc(kbd_codepage,0,&ch,1,&keys[r1],1);
+		}
+#ifdef SHOW_TOASCII_RESULT
+		debug((", ASC("));
+		for (r1 = 0; r1 < r; r1++) {
+		    debug(("%s%d", r1 ? ", " : "", ansikeys[r1]));
+		}
+		debug((")"));
+#endif
+		/* This is so the ALT-Numpad and dead keys work correctly. */
+		ansikeys[0] = 0;
+	    } else if(r == 0) {
+		/* If we're definitly not building up an ALT-54321 then clear it */
+		if (!left_alt)
+		    ansikeys[0] = 0;
+		/* If we will be using alt_sum fix the 256s XXX why are
+		 * we relying on the OS to detect keyup alt? */
+		else if (ansikeys[0] && (in_utf(term) || ucsdata.dbcs_screenfont))
+		    ansikeys[0] = 10;
+	    }
 	}
 #ifdef SHOW_TOASCII_RESULT
 	if (r == 1 && !key_down) {
@@ -4387,13 +4455,13 @@
 		else
 		    debug((", LCH(%d)", alt_sum));
 	    } else {
-		debug((", ACH(%d)", keys[0]));
+		debug((", ACH(U+%04x)", keys[0]));
 	    }
 	} else if (r > 0) {
 	    int r1;
-	    debug((", ASC("));
+	    debug((", UNI("));
 	    for (r1 = 0; r1 < r; r1++) {
-		debug(("%s%d", r1 ? "," : "", keys[r1]));
+		debug(("%s%04x", r1 ? ", U+" : "U+", keys[r1]));
 	    }
 	    debug((")"));
 	}
@@ -4410,14 +4478,14 @@
 
 	    p = output;
 	    for (i = 0; i < r; i++) {
-		unsigned char ch = (unsigned char) keys[i];
+		wchar_t ch = keys[i];
 
-		if (compose_state == 2 && (ch & 0x80) == 0 && ch > ' ') {
+		if (compose_state == 2 && ch < 0x80 && ch > ' ') {
 		    compose_char = ch;
 		    compose_state++;
 		    continue;
 		}
-		if (compose_state == 3 && (ch & 0x80) == 0 && ch > ' ') {
+		if (compose_state == 3 && ch < 0x80 && ch > ' ') {
 		    int nc;
 		    compose_state = 0;
 
@@ -4442,7 +4510,7 @@
 			    if (ldisc)
 				luni_send(ldisc, &keybuf, 1, 1);
 			} else {
-			    ch = (char) alt_sum;
+			    char ach = (char) alt_sum;
 			    /*
 			     * We need not bother about stdin
 			     * backlogs here, because in GUI PuTTY
@@ -4454,31 +4522,24 @@
 			     */
 			    term_seen_key_event(term);
 			    if (ldisc)
-				ldisc_send(ldisc, &ch, 1, 1);
+				ldisc_send(ldisc, &ach, 1, 1);
 			}
 			alt_sum = 0;
 		    } else {
 			term_seen_key_event(term);
 			if (ldisc)
-			    lpage_send(ldisc, kbd_codepage, &ch, 1, 1);
+			    luni_send(ldisc, &ch, 1, 1);
 		    }
 		} else {
-		    if(capsOn && ch < 0x80) {
-			WCHAR cbuf[2];
-			cbuf[0] = 27;
+		    wchar_t cbuf[2];
+		    cbuf[0] = L'\033';
+		    if(capsOn && ch < 0x80)
 			cbuf[1] = xlat_uskbd2cyrllic(ch);
-			term_seen_key_event(term);
-			if (ldisc)
-			    luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
-		    } else {
-			char cbuf[2];
-			cbuf[0] = '\033';
+		    else
 			cbuf[1] = ch;
-			term_seen_key_event(term);
-			if (ldisc)
-			    lpage_send(ldisc, kbd_codepage,
-				       cbuf+!left_alt, 1+!!left_alt, 1);
-		    }
+		    term_seen_key_event(term);
+		    if (ldisc)
+			luni_send(ldisc, cbuf+!left_alt, 1+!!left_alt, 1);
 		}
 		show_mouseptr(0);
 	    }
@@ -5123,6 +5184,17 @@
     cleanup_exit(1);
 }
 
+static void init_tounicode(void)
+{
+    /* Both 98 and NT 3.5 have some kind of stub that we DON'T want. */
+    if(osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT &&
+	    osVersion.dwMajorVersion >= 4) {
+	HMODULE user32_module = load_system32_dll("user32.dll");
+	GET_WINDOWS_FUNCTION(user32_module, ToUnicodeEx);
+    }
+}
+
+
 DECL_WINDOWS_FUNCTION(static, BOOL, FlashWindowEx, (PFLASHWINFO));
 
 static void init_flashwindow(void)
diff -ru clean/windows/winstuff.h unicode-input/windows/winstuff.h
--- clean/windows/winstuff.h	2011-02-16 21:41:58.543899800 -0500
+++ unicode-input/windows/winstuff.h	2011-02-18 19:45:18.406964500 -0500
@@ -39,6 +39,14 @@
 #define PLATFORM_IS_UTF16 /* enable UTF-16 processing when exchanging
 			   * wchar_t strings with environment */
 
+/* This is only generally defined with _WIN32_WINNT >= 0x0501 */
+#ifndef WM_UNICHAR
+#define WM_UNICHAR 0x0109
+#endif
+#ifndef UNICODE_NOCHAR
+#define UNICODE_NOCHAR 0xFFFF
+#endif
+
 /*
  * Where we can, we use GetWindowLongPtr and friends because they're
  * more useful on 64-bit platforms, but they're a relatively recent
