Index: src/Fl_x.cxx
===================================================================
--- src/Fl_x.cxx	(revision 8625)
+++ src/Fl_x.cxx	(working copy)
@@ -922,9 +922,6 @@
 #endif
   }
 
-  if ( XFilterEvent((XEvent *)&xevent, 0) )
-      return(1);
-
   switch (xevent.type) {
 
   case KeymapNotify:
@@ -1219,9 +1216,11 @@
   case KeyRelease: {
   KEYPRESS:
     int keycode = xevent.xkey.keycode;
+    static int real_keycode;
     fl_key_vector[keycode/8] |= (1 << (keycode%8));
     static char *buffer = NULL;
     static int buffer_len = 0;
+    static char *compose_buffer = NULL;
     int len;
     KeySym keysym;
     if (buffer_len == 0) {
@@ -1232,6 +1231,12 @@
       event = FL_KEYDOWN;
       int len = 0;
 
+      // keycode 0 is a fake key, so looking that up is pointless,
+      // instead we rely on the fact that the preceding event was the
+      // real key
+      if (keycode != 0)
+        real_keycode = keycode;
+
       if (fl_xim_ic) {
 	Status status;
 	len = XUtf8LookupString(fl_xim_ic, (XKeyPressedEvent *)&xevent.xkey,
@@ -1243,7 +1248,113 @@
 	  len = XUtf8LookupString(fl_xim_ic, (XKeyPressedEvent *)&xevent.xkey,
 			     buffer, buffer_len, &keysym, &status);
 	}
-	keysym = XKeycodeToKeysym(fl_display, keycode, 0);
+
+        // if this returns true, then Xlib has done some compose processing
+        if (XFilterEvent((XEvent *)&xevent, 0)) {
+          XEvent peekevent;
+
+          // store a copy of the current symbol as it'll get
+          // overwritten with the composed character in the next
+          // iteration
+          if (len) {
+            if (compose_buffer)
+              free(compose_buffer);
+            compose_buffer = strdup(buffer);
+
+            Fl::e_compose_symbol = compose_buffer;
+          } else {
+            // Xutf8LookupString() doesn't give us anything for dead
+            // symbols though, so we need to figure out the symbol
+            // ourselves using the keysym
+            switch (keysym) {
+            case XK_dead_grave:
+              Fl::e_compose_symbol = "\314\200";  // U+0300 COMBINING GRAVE ACCENT
+              break;
+            case XK_dead_acute:
+              Fl::e_compose_symbol = "\314\201";  // U+0301 COMBINING ACUTE ACCENT
+              break;
+            case XK_dead_circumflex:
+              Fl::e_compose_symbol = "\314\202";  // U+0302 COMBINING CIRCUMFLEX ACCENT
+              break;
+            case XK_dead_tilde:
+              Fl::e_compose_symbol = "\314\203";  // U+0303 COMBINING TILDE
+              break;
+            case XK_dead_macron:
+              Fl::e_compose_symbol = "\314\204";  // U+0304 COMBINING MACRON
+              break;
+            case XK_dead_breve:
+              Fl::e_compose_symbol = "\314\206";  // U+0306 COMBINING BREVE
+              break;
+            case XK_dead_abovedot:
+              Fl::e_compose_symbol = "\314\207";  // U+0307 COMBINING DOT ABOVE
+              break;
+            case XK_dead_diaeresis:
+              Fl::e_compose_symbol = "\314\210";  // U+0308 COMBINING DIAERESIS
+              break;
+            case XK_dead_abovering:
+              Fl::e_compose_symbol = "\314\212";  // U+030A COMBINING RING ABOVE
+              break;
+            case XK_dead_doubleacute:
+              Fl::e_compose_symbol = "\314\213";  // U+030B COMBINING DOUBLE ACUTE ACCENT
+              break;
+            case XK_dead_caron:
+              Fl::e_compose_symbol = "\314\214";  // U+030C COMBINING CARON
+              break;
+            case XK_dead_cedilla:
+              Fl::e_compose_symbol = "\314\247";  // U+0327 COMBINING CEDILLA
+              break;
+            case XK_dead_ogonek:
+              Fl::e_compose_symbol = "\314\250";  // U+0328 COMBINING OGONEK
+              break;
+            case XK_dead_iota:
+              Fl::e_compose_symbol = "\341\266\245";  // U+1DA5 MODIFIER LETTER SMALL IOTA
+              break;
+            case XK_dead_voiced_sound:
+              Fl::e_compose_symbol = "\343\202\231";  // U+3099 COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK
+              break;
+            case XK_dead_semivoiced_sound:
+              Fl::e_compose_symbol = "\343\202\232";  // U+309A COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+              break;
+            case XK_dead_belowdot:
+              Fl::e_compose_symbol = "\314\243";  // U+0323 COMBINING DOT BELOW
+              break;
+            default:
+              // this is some unknown compose character
+              Fl::e_compose_symbol = "";
+            }
+          }
+	  // We don't want to display anything for intermediate stages, so
+	  // clear out the event text
+	  len = 0;
+
+          // if a new character was produced then Xlib has put back an event
+          // on the queue with keycode 0
+          if (XPending(fl_display)) {
+            XPeekEvent(fl_display, &peekevent);
+            if (   (peekevent.type == KeyPress) // must be a KeyPress event
+                && (peekevent.xkey.keycode == 0) // must be keycode 0
+                && (peekevent.xkey.time == xevent.xkey.time) // must be sent at the exact same time
+                ) {
+              XNextEvent(fl_display, &xevent);
+
+              // Something went wrong fetching the current symbol...
+              if (Fl::e_compose_symbol[0] == '\0')
+                Fl::e_compose_state = FL_COMPOSE_UNKNOWN;
+              else
+                Fl::e_compose_state = FL_COMPOSE_FINAL;
+
+              // Process things again to get the composed symbol
+              goto KEYPRESS;
+            }
+          }
+          Fl::e_compose_state = FL_COMPOSE_INTERMEDIATE;
+        } else {
+          // if the keycode is 0 then state was set earlier
+          if (keycode != 0)
+            Fl::e_compose_state = FL_COMPOSE_NONE;
+        }
+
+        keysym = XKeycodeToKeysym(fl_display, real_keycode, 0);
       } else {
         //static XComposeStatus compose;
         len = XLookupString((XKeyEvent*)&(xevent.xkey),
Index: FL/Enumerations.H
===================================================================
--- FL/Enumerations.H	(revision 8623)
+++ FL/Enumerations.H	(working copy)
@@ -423,6 +423,25 @@
 
 /*@}*/		// group: Event States
 
+/** \name Compose States
+
+	These constants define the state of character composition for the
+	current keyboard event.
+	
+	\sa Fl::event_compose_state(), Fl::event_compose_symbol()
+*/
+
+/*@{*/
+
+enum Fl_Compose {
+  FL_COMPOSE_UNKNOWN = 0,	///< current compose status cannot be determined
+  FL_COMPOSE_NONE,		///< no composition active for this event
+  FL_COMPOSE_INTERMEDIATE,	///< the current event is part of a composition but does not end it
+  FL_COMPOSE_FINAL,		///< a composed character has successfully been constructed
+};
+
+/*@}*/		// group: Compose States
+
 /** \name Box Types
     \brief FLTK standard box types
     
Index: FL/Fl.H
===================================================================
--- FL/Fl.H	(revision 8548)
+++ FL/Fl.H	(working copy)
@@ -133,6 +133,8 @@
   static int e_keysym;
   static char* e_text;
   static int e_length;
+  static int e_compose_state;
+  static const char* e_compose_symbol;
   static Fl_Event_Dispatch e_dispatch;
   static Fl_Widget* belowmouse_;
   static Fl_Widget* pushed_;
@@ -685,6 +687,9 @@
   */
   static int event_length() {return e_length;}
 
+  static int event_compose_state();
+  static const char* event_compose_symbol();
+
   static int compose(int &del);
   static void compose_reset();
   static int event_inside(int,int,int,int);
Index: src/Fl_compose.cxx
===================================================================
--- src/Fl_compose.cxx	(revision 8626)
+++ src/Fl_compose.cxx	(working copy)
@@ -30,6 +30,8 @@
 
 #ifndef FL_DOXYGEN
 int Fl::compose_state = 0;
+int Fl::e_compose_state = FL_COMPOSE_UNKNOWN;
+const char* Fl::e_compose_symbol = "";
 #endif
 
 #if !defined(WIN32) && !defined(__APPLE__)
@@ -96,6 +98,31 @@
 #endif
 }
 
+/**
+  Returns what role the current key press plays in constructing a composed
+  character.
+  \see Fl_Compose
+ */
+int Fl::event_compose_state()
+{
+  return e_compose_state;
+}
+
+/**
+  Returns the raw symbol associated with the current event. This will be
+  the same as what Fl::event_text() returns except when the compose state
+  is FL_COMPOSE_INTERMEDIATE or FL_COMPOSE_FINAL.
+  \see Fl_Compose
+ */
+const char* Fl::event_compose_symbol()
+{
+  if ((e_compose_state == FL_COMPOSE_INTERMEDIATE) ||
+      (e_compose_state == FL_COMPOSE_FINAL))
+    return e_compose_symbol;
+
+  return Fl::event_text();
+}
+
 //
 // End of "$Id$"
 //
Index: src/Fl_win32.cxx
===================================================================
--- src/Fl_win32.cxx	(revision 8623)
+++ src/Fl_win32.cxx	(working copy)
@@ -778,6 +778,27 @@
   return extended ? extendedlut[vk] : vklut[vk];
 }
 
+static xchar msdead2fltk(xchar in)
+{
+  switch (in) {
+  case 0x0060:      // GRAVE ACCENT
+    return 0x0300;  // COMBINING GRAVE ACCENT
+  case 0x00b4:      // ACUTE ACCENT
+    return 0x0301;  // COMBINING ACUTE ACCENT
+  case 0x005e:      // CIRCUMFLEX ACCENT
+    return 0x0302;  // COMBINING CIRCUMFLEX ACCENT
+  case 0x007e:      // TILDE
+    return 0x0303;  // COMBINING TILDE
+  case 0x00a8:      // DIAERESIS
+    return 0x0308;  // COMBINING DIAERESIS
+  // FIXME: Windows dead key behaviour isn't documented and I don't have
+  //        any more keyboards to test with...
+  }
+
+  // hope that Windows gave us something proper to begin with
+  return in;
+}
+
 #if USE_COLORMAP
 extern HPALETTE fl_select_palette(void); // in fl_color_win32.cxx
 #endif
@@ -839,6 +860,8 @@
   //fl_msg.pt = ???
   //fl_msg.lPrivate = ???
 
+  MSG fl_orig_msg = fl_msg;
+
   Fl_Window *window = fl_find(hWnd);
 
   if (window) switch (uMsg) {
@@ -1018,6 +1041,7 @@
     if (GetKeyState(VK_SCROLL)) state |= FL_SCROLL_LOCK;
     Fl::e_state = state;
     static char buffer[1024];
+    static char compose_buffer[1024];
     if (uMsg == WM_CHAR || uMsg == WM_SYSCHAR) {
 
       xchar u = (xchar) wParam;
@@ -1025,7 +1049,67 @@
       Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1);
       buffer[Fl::e_length] = 0;
 
+      if (Fl::e_compose_state == FL_COMPOSE_INTERMEDIATE) {
+        // if you enter an invalid compose sequence Windows will spit
+        // out the entire thing in back-to-back messages
+        while (PeekMessageW(&fl_msg, hWnd, WM_CHAR, WM_SYSDEADCHAR, PM_REMOVE)) {
+          uMsg = fl_msg.message;
+          wParam = fl_msg.wParam;
+          lParam = fl_msg.lParam;
 
+          u = (xchar) wParam;
+          Fl::e_length += fl_utf8fromwc(&buffer[Fl::e_length], 1024-Fl::e_length, &u, 1);
+          buffer[Fl::e_length] = 0;
+
+          // update the compose symbol so that it will end up pointing at
+          // the last symbol in the sequence
+          u = msdead2fltk(u);
+          int length = fl_utf8fromwc(compose_buffer, 1024, &u, 1);
+          compose_buffer[length] = '\0';
+
+          Fl::e_compose_symbol = compose_buffer;
+          Fl::e_compose_state = FL_COMPOSE_FINAL;
+        }
+
+        if (Fl::e_compose_state != FL_COMPOSE_FINAL) {
+          MSG extra_msg;
+
+          // Windows doesn't expose the raw symbol (AFAIK), so we have to
+          // get a bit creative. Resend the last message to TranslateMessage()
+          // without all the inital dead characters, and we should get the
+          // raw symbol.
+          TranslateMessage(&fl_orig_msg);
+
+          if (PeekMessageW(&extra_msg, hWnd, WM_CHAR, WM_SYSDEADCHAR, PM_REMOVE)) {
+            u = (xchar) extra_msg.wParam;
+            int length = fl_utf8fromwc(compose_buffer, 1024, &u, 1);
+            compose_buffer[length] = '\0';
+
+            Fl::e_compose_symbol = compose_buffer;
+            Fl::e_compose_state = FL_COMPOSE_FINAL;
+          } else {
+            // something went wrong, just provide the composed symbol
+            Fl::e_compose_symbol = buffer;
+            Fl::e_compose_state = FL_COMPOSE_FINAL;
+          }
+        }
+      } else {
+        Fl::e_compose_state = FL_COMPOSE_NONE;
+      }
+    } else if (uMsg == WM_DEADCHAR || uMsg == WM_SYSDEADCHAR) {
+      xchar u = (xchar) wParam;
+      int length;
+
+      u = msdead2fltk(u);
+
+      length = fl_utf8fromwc(compose_buffer, 1024, &u, 1);
+      compose_buffer[length] = '\0';
+
+      Fl::e_compose_state = FL_COMPOSE_INTERMEDIATE;
+      Fl::e_compose_symbol = compose_buffer;
+
+      buffer[0] = '\0';
+      Fl::e_length = 0;
     } else if (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last) {
       if (state & FL_NUM_LOCK) {
         // Convert to regular keypress...
@@ -1076,19 +1160,8 @@
 	}
       }
     } else if ((lParam & (1<<31))==0) {
-#ifdef FLTK_PREVIEW_DEAD_KEYS
-      if ((lParam & (1<<24))==0) { // clear if dead key (always?)
-        xchar u = (xchar) wParam;
-        Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1);
-        buffer[Fl::e_length] = 0;
-      } else { // set if "extended key" (never printable?)
-        buffer[0] = 0;
-        Fl::e_length = 0;
-      }
-#else
       buffer[0] = 0;
       Fl::e_length = 0;
-#endif
     }
     Fl::e_text = buffer;
     if (lParam & (1<<31)) { // key up events.
