diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c
index 1cde4fc..041b440 100644
--- a/gdk/win32/gdkevents-win32.c
+++ b/gdk/win32/gdkevents-win32.c
@@ -2029,6 +2029,115 @@ ensure_stacking_on_activate_app (MSG       *msg,
     }
 }
 
+static gboolean
+wm_char_wparam_needs_translation (WPARAM wParam)
+{
+  switch (wParam)
+    {
+    case VK_SHIFT:
+    case VK_RETURN:
+    case VK_TAB:
+    case VK_BACK:
+      return TRUE;
+
+    default:
+      return FALSE;
+    }
+}
+
+static void
+emit_gdk_key_event (MSG *msg,
+                    gint gdk_evt_type,
+		    guint16 hardware_keycode,
+                    GdkWindow *window,
+                    GdkKeyboardGrabInfo keyboard_grab)
+{
+  GdkEvent *event = NULL;
+  wchar_t wbuf[100];
+  BYTE    key_state[256];
+
+  /* Ignore key messages intended for the IME */
+  if (in_ime_composition || msg->wParam == VK_PROCESSKEY)
+    return;
+
+  if (!propagate (&window, msg,
+		  keyboard_grab.window,
+		  keyboard_grab.owner_events,
+		  GDK_ALL_EVENTS_MASK,
+		  doesnt_want_key))
+    return;
+
+  if (GDK_WINDOW_DESTROYED (window))
+    return;
+
+  event = gdk_event_new (gdk_evt_type);
+  event->key.window           = window;
+  event->key.time             = _gdk_win32_get_next_tick (msg->time);
+  event->key.keyval           = GDK_KEY_VoidSymbol;
+  event->key.string           = NULL;
+  event->key.length           = 0;
+  event->key.hardware_keycode = msg->wParam;
+
+  if (HIWORD (msg->lParam) & KF_EXTENDED)
+    {
+      switch (msg->wParam)
+        {
+        case VK_CONTROL:
+          event->key.hardware_keycode = VK_RCONTROL;
+          break;
+
+        case VK_SHIFT:
+          /* Actually, KF_EXTENDED is not set for the right shift key */
+          event->key.hardware_keycode = VK_RSHIFT;
+          break;
+
+        case VK_MENU:
+          event->key.hardware_keycode = VK_RMENU;
+          break;
+        }
+    }
+  else if (msg->wParam == VK_SHIFT && LOBYTE (HIWORD (msg->lParam)) == _scancode_rshift)
+    {
+      event->key.hardware_keycode = VK_RSHIFT;
+    }
+
+  API_CALL (GetKeyboardState, (key_state));
+
+  build_key_event_state (event, key_state);
+
+  if (msg->message == WM_CHAR && !wm_char_wparam_needs_translation (msg->wParam))
+    {
+      event->key.keyval = msg->wParam;
+      event->key.hardware_keycode = 0;
+      //event->key.hardware_keycode = hardware_keycode;
+    }
+  else
+    {
+      if (msg->wParam == VK_PACKET &&
+          ToUnicode (VK_PACKET, HIWORD (msg->lParam), key_state, wbuf, 1, 0) == 1)
+        {
+          event->key.keyval = gdk_unicode_to_keyval (wbuf[0]);
+        }
+      else
+        {
+          gdk_keymap_translate_keyboard_state (NULL,
+                                               event->key.hardware_keycode,
+                                               event->key.state,
+                                               event->key.group,
+                                               &event->key.keyval,
+                                               NULL, NULL, NULL);
+        }
+    }
+
+  fill_key_event_string (event);
+
+  /* Reset MOD1_MASK if it is the Alt key itself */
+  if (msg->wParam == VK_MENU)
+    event->key.state &= ~GDK_MOD1_MASK;
+
+  _gdk_win32_append_event (event);
+}
+
 #define GDK_ANY_BUTTON_MASK (GDK_BUTTON1_MASK | \
 			     GDK_BUTTON2_MASK | \
 			     GDK_BUTTON3_MASK | \
@@ -2048,6 +2157,8 @@ gdk_event_translate (MSG  *msg,
   HIMC himc;
   WINDOWPOS *windowpos;
   gboolean ignore_leave;
+  MSG msg2;
+  gboolean msg2_res;
 
   GdkEvent *event;
 
@@ -2057,7 +2168,7 @@ gdk_event_translate (MSG  *msg,
   GdkWindow *window = NULL;
   GdkWindowImplWin32 *impl;
 
-  GdkWindow *orig_window, *new_window;
+  GdkWindow *new_window;
 
   GdkPointerGrabInfo *grab = NULL;
   GdkWindow *grab_window = NULL;
@@ -2071,6 +2182,13 @@ gdk_event_translate (MSG  *msg,
 
   int i;
 
+  /* Internal state about keyboard sequence for dead key input */
+  static gboolean have_dead = FALSE;
+  static gboolean finished_dead = FALSE;
+  static gboolean in_dead_sequence = FALSE;
+  static gboolean awaiting_next_in_sequence = FALSE;
+  static guint16  last_hardware_keycode = 0;
+
   if (_gdk_default_filters)
     {
       /* Apply global filters */
@@ -2086,7 +2204,6 @@ gdk_event_translate (MSG  *msg,
     }
 
   window = gdk_win32_handle_table_lookup ((GdkNativeWindow) msg->hwnd);
-  orig_window = window;
 
   if (window == NULL)
     {
@@ -2231,10 +2348,13 @@ gdk_event_translate (MSG  *msg,
 	   msg->wParam == VK_F4))
 	break;
 
-      /* Jump to code in common with WM_KEYUP and WM_KEYDOWN */
-      goto keyup_or_down;
+      emit_gdk_key_event (msg,
+			  GDK_KEY_PRESS,
+			  0,
+			  window,
+			  _gdk_display->keyboard_grab);
+      break;
 
-    case WM_KEYUP:
     case WM_KEYDOWN:
       GDK_NOTE (EVENTS, 
 		g_print (" %s ch:%.02x %s",
@@ -2242,77 +2362,96 @@ gdk_event_translate (MSG  *msg,
 			 (int) msg->wParam,
 			 decode_key_lparam (msg->lParam)));
 
-    keyup_or_down:
+      if (PeekMessageW (&msg2, NULL, WM_DEADCHAR, WM_DEADCHAR, PM_NOREMOVE))
+	{
+	  have_dead = in_dead_sequence = TRUE;
+	  return_val = FALSE;
+	  break;
+	}
+      else if (have_dead && PeekMessageW (&msg2, NULL, WM_CHAR, WM_CHAR, PM_NOREMOVE))
+	{
+	  break;
+	}
+
+      last_hardware_keycode = msg->wParam;
 
-      /* Ignore key messages intended for the IME */
-      if (msg->wParam == VK_PROCESSKEY ||
-	  in_ime_composition)
-	break;
+      msg2_res = PeekMessageW (&msg2, NULL, WM_CHAR, WM_CHAR, PM_NOREMOVE);
+      API_CALL (GetKeyboardState, (key_state));
+      if (!msg2_res ||
+	  (key_state[VK_SHIFT] & 0x80) ||
+	  (key_state[VK_LCONTROL] & 0x80) ||
+	  (key_state[VK_RMENU] & 0x80) ||
+	  (key_state[VK_RCONTROL] & 0x80) ||
+	  (key_state[VK_LMENU] & 0x80) ||
+	  (key_state[VK_CONTROL] & 0x80) ||
+	  (key_state[VK_MENU] & 0x80))
+	{
+	  /* Eat the WM_CHAR message and emit it right now. */
+	  PeekMessageW (&msg2, NULL, WM_CHAR, WM_CHAR, PM_REMOVE);
 
-      if (!propagate (&window, msg,
-		      _gdk_display->keyboard_grab.window,
-		      _gdk_display->keyboard_grab.owner_events,
-		      GDK_ALL_EVENTS_MASK,
-		      doesnt_want_key))
-	break;
+	  emit_gdk_key_event (msg,
+			      GDK_KEY_PRESS,
+			      msg->wParam,
+			      window,
+			      _gdk_display->keyboard_grab);
+	}
 
-      if (GDK_WINDOW_DESTROYED (window))
-	break;
+      break;
 
-      event = gdk_event_new ((msg->message == WM_KEYDOWN ||
-			      msg->message == WM_SYSKEYDOWN) ?
-			     GDK_KEY_PRESS : GDK_KEY_RELEASE);
-      event->key.window = window;
-      event->key.time = _gdk_win32_get_next_tick (msg->time);
-      event->key.keyval = GDK_VoidSymbol;
-      event->key.string = NULL;
-      event->key.length = 0;
-      event->key.hardware_keycode = msg->wParam;
-      if (HIWORD (msg->lParam) & KF_EXTENDED)
+    case WM_KEYUP:
+      if (in_dead_sequence)
 	{
-	  switch (msg->wParam)
+	  if (finished_dead)
 	    {
-	    case VK_CONTROL:
-	      event->key.hardware_keycode = VK_RCONTROL;
-	      break;
-	    case VK_SHIFT:	/* Actually, KF_EXTENDED is not set
-				 * for the right shift key.
-				 */
-	      event->key.hardware_keycode = VK_RSHIFT;
-	      break;
-	    case VK_MENU:
-	      event->key.hardware_keycode = VK_RMENU;
-	      break;
+	      if (!awaiting_next_in_sequence)
+		{
+		  emit_gdk_key_event (msg,
+				      GDK_KEY_RELEASE,
+				      msg->wParam,
+				      window,
+				      _gdk_display->keyboard_grab);
+		  in_dead_sequence = finished_dead = have_dead = FALSE;
+		  return_val = TRUE;
+		  break;
+		}
+	      else
+		{
+		  awaiting_next_in_sequence = FALSE;
+		}
+	    }
+	  else
+	    {
+	      finished_dead = TRUE;
+	      awaiting_next_in_sequence = TRUE;
 	    }
-	}
-      else if (msg->wParam == VK_SHIFT &&
-	       LOBYTE (HIWORD (msg->lParam)) == _scancode_rshift)
-	event->key.hardware_keycode = VK_RSHIFT;
 
-      API_CALL (GetKeyboardState, (key_state));
+	  break;
+	}
 
-      /* g_print ("ctrl:%02x lctrl:%02x rctrl:%02x alt:%02x lalt:%02x ralt:%02x\n", key_state[VK_CONTROL], key_state[VK_LCONTROL], key_state[VK_RCONTROL], key_state[VK_MENU], key_state[VK_LMENU], key_state[VK_RMENU]); */
-      
-      build_key_event_state (event, key_state);
+      if (!PeekMessageW (&msg2, NULL, WM_CHAR, WM_CHAR, PM_NOREMOVE))
+	{
+	  emit_gdk_key_event (msg,
+			      GDK_KEY_RELEASE,
+			      msg->wParam,
+			      window,
+			      _gdk_display->keyboard_grab);
+	}
 
-      if (msg->wParam == VK_PACKET &&
-	  ToUnicode (VK_PACKET, HIWORD (msg->lParam), key_state, wbuf, 1, 0) == 1)
-	event->key.keyval = gdk_unicode_to_keyval (wbuf[0]);
-      else
-	gdk_keymap_translate_keyboard_state (NULL,
-					     event->key.hardware_keycode,
-					     event->key.state,
-					     event->key.group,
-					     &event->key.keyval,
-					     NULL, NULL, NULL);
+      last_hardware_keycode = 0;
 
-      fill_key_event_string (event);
+      break;
 
-      /* Reset MOD1_MASK if it is the Alt key itself */
-      if (msg->wParam == VK_MENU)
-	event->key.state &= ~GDK_MOD1_MASK;
+    case WM_CHAR:
+      if (in_dead_sequence)
+	{
+	  finished_dead = TRUE;
+	}
 
-      _gdk_win32_append_event (event);
+      emit_gdk_key_event (msg,
+			  GDK_KEY_PRESS,
+			  last_hardware_keycode,
+			  window,
+			  _gdk_display->keyboard_grab);
 
       return_val = TRUE;
       break;
