Remove simple keyboard model

Remove the FLTK simple keyboard system and reorganise things in
preparation for a more direct approach.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ce0a00a..30520a5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -279,9 +279,6 @@
   set(CMAKE_REQUIRED_INCLUDES ${FLTK_INCLUDE_DIR})
   set(CMAKE_REQUIRED_LIBRARIES ${FLTK_LIBRARIES})
 
-  # FLTK STR #2599
-  check_cxx_source_compiles("#include <FL/Fl_Widget.H>\nint main(int c, char** v) { void (Fl_Widget::*foo)() = &Fl_Widget::set_simple_keyboard; return 0; }" HAVE_FLTK_DEAD_KEYS)
-
   # FLTK STR #2636
   check_cxx_source_compiles("#include <FL/Fl.H>\nint main(int c, char** v) { Fl::add_clipboard_notify(NULL, NULL); return 0; }" HAVE_FLTK_CLIPBOARD)
 
diff --git a/config.h.in b/config.h.in
index 0c9e71a..a88c31c 100644
--- a/config.h.in
+++ b/config.h.in
@@ -10,7 +10,6 @@
 #cmakedefine HAVE_GNUTLS_DATUM_T
 #cmakedefine HAVE_GNUTLS_PK_ALGORITHM_T
 #cmakedefine HAVE_GNUTLS_SIGN_ALGORITHM_T
-#cmakedefine HAVE_FLTK_DEAD_KEYS
 #cmakedefine HAVE_FLTK_CLIPBOARD
 #cmakedefine HAVE_FLTK_MEDIAKEYS
 #cmakedefine HAVE_FLTK_FULLSCREEN
diff --git a/contrib/fltk/01-str2599-fltk-1.3.x-keyboard-x11.patch b/contrib/fltk/01-str2599-fltk-1.3.x-keyboard-x11.patch
deleted file mode 100644
index cabc0f1..0000000
--- a/contrib/fltk/01-str2599-fltk-1.3.x-keyboard-x11.patch
+++ /dev/null
@@ -1,286 +0,0 @@
-diff -ur fltk-1.3.0r9619.org/FL/Fl_Widget.H fltk-1.3.0r9619/FL/Fl_Widget.H
---- fltk-1.3.0r9619.org/FL/Fl_Widget.H	2012-04-23 22:12:06.000000000 +0200
-+++ fltk-1.3.0r9619/FL/Fl_Widget.H	2012-06-18 13:46:07.302320825 +0200
-@@ -171,6 +171,7 @@
-         GROUP_RELATIVE  = 1<<16,  ///< position this widget relative to the parent group, not to the window
-         COPIED_TOOLTIP  = 1<<17,  ///< the widget tooltip is internally copied, its destruction is handled by the widget
-         FULLSCREEN      = 1<<18,  ///< a fullscreen window (Fl_Window)
-+        SIMPLE_KEYBOARD = 1<<19,  ///< the widget wants simple, consistent keypresses and not advanced input (like character composition and CJK input)
-         // (space for more flags)
-         USERFLAG3       = 1<<29,  ///< reserved for 3rd party extensions
-         USERFLAG2       = 1<<30,  ///< reserved for 3rd party extensions
-@@ -776,6 +777,35 @@
-    */
-   void clear_changed() {flags_ &= ~CHANGED;}
- 
-+  /** 
-+      Returns if the widget sees a simplified keyboard model or not.
-+
-+      Normally widgets get a full-featured keyboard model that is geared
-+      towards text input. This includes support for compose sequences and
-+      advanced input methods, commonly used for asian writing system. This
-+      system however has downsides in that extra graphic can be presented
-+      to the user and that a physical key press doesn't correspond directly
-+      to a FLTK event.
-+
-+      Widgets that need a direct correspondence between actual key events
-+      and those seen by the widget can swith to the simplified keyboard
-+      model.
-+
-+     \retval 0 if the widget uses the normal keyboard model
-+     \see set_changed(), clear_changed()
-+   */
-+  unsigned int simple_keyboard() const {return flags_&SIMPLE_KEYBOARD;}
-+
-+  /** Marks a widget to use the simple keyboard model.
-+      \see changed(), clear_changed()
-+   */
-+  void set_simple_keyboard() {flags_ |= SIMPLE_KEYBOARD;}
-+
-+  /** Marks a widget to use the normal keyboard model.
-+      \see changed(), set_changed()
-+   */
-+  void set_normal_keyboard() {flags_ &= ~SIMPLE_KEYBOARD;}
-+
-   /** Gives the widget the keyboard focus.
-       Tries to make this widget be the Fl::focus() widget, by first sending 
-       it an FL_FOCUS event, and if it returns non-zero, setting 
-diff -ur fltk-1.3.0r9619.org/src/Fl.cxx fltk-1.3.0r9619/src/Fl.cxx
---- fltk-1.3.0r9619.org/src/Fl.cxx	2012-03-23 17:47:53.000000000 +0100
-+++ fltk-1.3.0r9619/src/Fl.cxx	2012-06-18 13:46:07.303320877 +0200
-@@ -70,6 +70,8 @@
- extern double fl_mac_flush_and_wait(double time_to_wait, char in_idle);
- #endif // WIN32
- 
-+extern void fl_update_focus(void);
-+
- //
- // Globals...
- //
-@@ -876,6 +878,8 @@
-       fl_oldfocus = p;
-     }
-     e_number = old_event;
-+    // let the platform code do what it needs
-+    fl_update_focus();
-   }
- }
- 
-diff -ur fltk-1.3.0r9619.org/src/Fl_grab.cxx fltk-1.3.0r9619/src/Fl_grab.cxx
---- fltk-1.3.0r9619.org/src/Fl_grab.cxx	2012-03-23 17:47:53.000000000 +0100
-+++ fltk-1.3.0r9619/src/Fl_grab.cxx	2012-06-18 13:46:07.303320877 +0200
-@@ -29,6 +29,7 @@
- // override_redirect, it does similar things on WIN32.
- 
- extern void fl_fix_focus(); // in Fl.cxx
-+void fl_update_focus(void);
- 
- #ifdef WIN32
- // We have to keep track of whether we have captured the mouse, since
-@@ -80,6 +81,7 @@
- #endif
-     }
-     grab_ = win;
-+    fl_update_focus();
-   } else {
-     if (grab_) {
- #ifdef WIN32
-@@ -98,6 +100,7 @@
-       XFlush(fl_display);
- #endif
-       grab_ = 0;
-+      fl_update_focus();
-       fl_fix_focus();
-     }
-   }
-diff -ur fltk-1.3.0r9619.org/src/Fl_x.cxx fltk-1.3.0r9619/src/Fl_x.cxx
---- fltk-1.3.0r9619.org/src/Fl_x.cxx	2012-06-18 13:46:07.205316173 +0200
-+++ fltk-1.3.0r9619/src/Fl_x.cxx	2012-06-18 13:46:18.216844629 +0200
-@@ -298,6 +298,7 @@
- Colormap fl_colormap;
- XIM fl_xim_im = 0;
- XIC fl_xim_ic = 0;
-+Window fl_xim_win = 0;
- char fl_is_over_the_spot = 0;
- static XRectangle status_area;
- 
-@@ -583,6 +584,65 @@
-   if(xim_styles) XFree(xim_styles);
- }
- 
-+void fl_xim_deactivate(void);
-+
-+void fl_xim_activate(Window xid)
-+{
-+  if (!fl_xim_im)
-+    return;
-+
-+  // If the focused window has changed, then use the brute force method
-+  // of completely recreating the input context.
-+  if (fl_xim_win != xid) {
-+    fl_xim_deactivate();
-+
-+    fl_new_ic();
-+    fl_xim_win = xid;
-+
-+    XSetICValues(fl_xim_ic,
-+                 XNFocusWindow, fl_xim_win,
-+                 XNClientWindow, fl_xim_win,
-+                 NULL);
-+  }
-+
-+  fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
-+}
-+
-+void fl_xim_deactivate(void)
-+{
-+  if (!fl_xim_ic)
-+    return;
-+
-+  XDestroyIC(fl_xim_ic);
-+  fl_xim_ic = NULL;
-+
-+  fl_xim_win = 0;
-+}
-+
-+extern Fl_Window *fl_xfocus;
-+
-+void fl_update_focus(void)
-+{
-+  Fl_Widget *focus;
-+
-+  focus = Fl::grab();
-+  if (!focus)
-+    focus = Fl::focus();
-+  if (!focus)
-+    return;
-+
-+  if (focus->simple_keyboard()) {
-+    fl_xim_deactivate();
-+  } else {
-+    // fl_xfocus should always be set if something has focus, but let's
-+    // play it safe
-+    if (!fl_xfocus || !fl_xid(fl_xfocus))
-+      return;
-+
-+    fl_xim_activate(fl_xid(fl_xfocus));
-+  }
-+}
-+
- void fl_open_display() {
-   if (fl_display) return;
- 
-@@ -917,10 +977,9 @@
-   XEvent xevent = thisevent;
-   fl_xevent = &thisevent;
-   Window xid = xevent.xany.window;
--  static Window xim_win = 0;
- 
-   if (fl_xim_ic && xevent.type == DestroyNotify &&
--        xid != xim_win && !fl_find(xid))
-+        xid != fl_xim_win && !fl_find(xid))
-   {
-     XIM xim_im;
-     xim_im = XOpenIM(fl_display, NULL, NULL, NULL);
-@@ -935,48 +994,10 @@
-     return 0;
-   }
- 
--  if (fl_xim_ic && (xevent.type == FocusIn))
--  {
--#define POOR_XIM
--#ifdef POOR_XIM
--        if (xim_win != xid)
--        {
--                xim_win  = xid;
--                XDestroyIC(fl_xim_ic);
--                fl_xim_ic = NULL;
--                fl_new_ic();
--                XSetICValues(fl_xim_ic,
--                                XNFocusWindow, xevent.xclient.window,
--                                XNClientWindow, xid,
--                                NULL);
--        }
--        fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
--#else
--    if (Fl::first_window() && Fl::first_window()->modal()) {
--      Window x  = fl_xid(Fl::first_window());
--      if (x != xim_win) {
--        xim_win  = x;
--        XSetICValues(fl_xim_ic,
--                        XNFocusWindow, xim_win,
--                        XNClientWindow, xim_win,
--                        NULL);
--        fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
--      }
--    } else if (xim_win != xid && xid) {
--      xim_win = xid;
--      XSetICValues(fl_xim_ic,
--                        XNFocusWindow, xevent.xclient.window,
--                        XNClientWindow, xid,
--                        //XNFocusWindow, xim_win,
--                        //XNClientWindow, xim_win,
--                        NULL);
--      fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
--    }
--#endif
-+  if (fl_xim_ic) {
-+    if (XFilterEvent((XEvent *)&xevent, 0))
-+      return 1;
-   }
--
--  if ( XFilterEvent((XEvent *)&xevent, 0) )
--      return(1);
-   
- #if USE_XRANDR  
-   if( XRRUpdateConfiguration_f && xevent.type == randrEventBase + RRScreenChangeNotify) {
-@@ -1326,15 +1347,15 @@
-         //static XComposeStatus compose;
-         len = XLookupString((XKeyEvent*)&(xevent.xkey),
-                              buffer, buffer_len, &keysym, 0/*&compose*/);
--        if (keysym && keysym < 0x400) { // a character in latin-1,2,3,4 sets
--          // force it to type a character (not sure if this ever is needed):
--          // if (!len) {buffer[0] = char(keysym); len = 1;}
--          len = fl_utf8encode(XKeysymToUcs(keysym), buffer);
--          if (len < 1) len = 1;
--          // ignore all effects of shift on the keysyms, which makes it a lot
--          // easier to program shortcuts and is Windoze-compatible:
--          keysym = XKeycodeToKeysym(fl_display, keycode, 0);
--        }
-+        // XLookupString() is only defined to return Latin-1 (although it
-+        // often gives you more). To be safe, use our own lookups based on
-+        // keysym.
-+        len = fl_utf8encode(XKeysymToUcs(keysym), buffer);
-+        if (len < 1)
-+          len = 1;
-+        // ignore all effects of shift on the keysyms, which makes it a lot
-+        // easier to program shortcuts and is Windoze-compatable:
-+        keysym = XKeycodeToKeysym(fl_display, keycode, 0);
-       }
-       // MRS: Can't use Fl::event_state(FL_CTRL) since the state is not
-       //      set until set_event_xy() is called later...
-diff -ur fltk-1.3.0r9619.org/src/xutf8/imKStoUCS.c fltk-1.3.0r9619/src/xutf8/imKStoUCS.c
---- fltk-1.3.0r9619.org/src/xutf8/imKStoUCS.c	2009-03-13 23:43:43.000000000 +0100
-+++ fltk-1.3.0r9619/src/xutf8/imKStoUCS.c	2012-06-18 13:46:07.304320930 +0200
-@@ -266,6 +266,12 @@
-     0x20a8, 0x20a9, 0x20aa, 0x20ab, 0x20ac                          /* 0x20a8-0x20af */
- };
- 
-+static unsigned short const keysym_to_unicode_fe50_fe60[] = {
-+    0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0306, 0x0307, 0x0308, /* 0xfe50-0xfe57 */
-+    0x030a, 0x030b, 0x030c, 0x0327, 0x0328, 0x1da5, 0x3099, 0x309a, /* 0xfe58-0xfe5f */
-+    0x0323                                                          /* 0xfe60-0xfe67 */
-+};
-+
- unsigned int
- KeySymToUcs4(KeySym keysym)
- {
-@@ -315,6 +321,8 @@
- 	return keysym_to_unicode_1e9f_1eff[keysym - 0x1e9f];
-     else if (keysym > 0x209f && keysym < 0x20ad)
- 	return keysym_to_unicode_20a0_20ac[keysym - 0x20a0];
-+    else if (keysym > 0xfe4f && keysym < 0xfe61)
-+	return keysym_to_unicode_fe50_fe60[keysym - 0xfe50];
-     else 
- 	return 0;
- }
diff --git a/contrib/fltk/04-str2636-fltk-1.3.x-clipboard.patch b/contrib/fltk/01-str2636-fltk-1.3.x-clipboard.patch
similarity index 100%
rename from contrib/fltk/04-str2636-fltk-1.3.x-clipboard.patch
rename to contrib/fltk/01-str2636-fltk-1.3.x-clipboard.patch
diff --git a/contrib/fltk/02-str2599-fltk-1.3.x-keyboard-win32.patch b/contrib/fltk/02-str2599-fltk-1.3.x-keyboard-win32.patch
deleted file mode 100644
index c29d3b9..0000000
--- a/contrib/fltk/02-str2599-fltk-1.3.x-keyboard-win32.patch
+++ /dev/null
@@ -1,256 +0,0 @@
-diff -ur fltk-1.3.0r9293.org/src/Fl_win32.cxx fltk-1.3.0r9293/src/Fl_win32.cxx
---- fltk-1.3.0r9293.org/src/Fl_win32.cxx	2012-06-18 09:07:56.522314557 +0200
-+++ fltk-1.3.0r9293/src/Fl_win32.cxx	2012-06-18 09:08:07.392836285 +0200
-@@ -87,6 +87,8 @@
- static Fl_Display_Device fl_gdi_display(&fl_gdi_driver);
- Fl_Display_Device *Fl_Display_Device::_display = &fl_gdi_display; // the platform display
- 
-+bool use_simple_keyboard = false;
-+
- // dynamic wsock dll handling api:
- #if defined(__CYGWIN__) && !defined(SOCKET)
- # define SOCKET int
-@@ -120,6 +122,8 @@
-  * size and link dependencies.
-  */
- static HMODULE s_imm_module = 0;
-+typedef BOOL (WINAPI* flTypeImmAssociateContextEx)(HWND, HIMC, DWORD);
-+static flTypeImmAssociateContextEx flImmAssociateContextEx = 0;
- typedef HIMC (WINAPI* flTypeImmGetContext)(HWND);
- static flTypeImmGetContext flImmGetContext = 0;
- typedef BOOL (WINAPI* flTypeImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM);
-@@ -135,6 +139,7 @@
-     if (!s_imm_module)
-       Fl::fatal("FLTK Lib Error: IMM32.DLL file not found!\n\n"
-         "Please check your input method manager library accessibility.");
-+    flImmAssociateContextEx = (flTypeImmAssociateContextEx)GetProcAddress(s_imm_module, "ImmAssociateContextEx");
-     flImmGetContext = (flTypeImmGetContext)GetProcAddress(s_imm_module, "ImmGetContext");
-     flImmSetCompositionWindow = (flTypeImmSetCompositionWindow)GetProcAddress(s_imm_module, "ImmSetCompositionWindow");
-     flImmReleaseContext = (flTypeImmReleaseContext)GetProcAddress(s_imm_module, "ImmReleaseContext");
-@@ -413,7 +418,12 @@
-         }
-       }
- 
--      TranslateMessage(&fl_msg);
-+      // Don't bother with key to character translation as we do
-+      // it manually for simpley keyboard widgets. In fact, calling
-+      // TranslateMessage() just makes it more difficult as it sets
-+      // a bunch of internal state.
-+      if (!use_simple_keyboard)
-+        TranslateMessage(&fl_msg);
-       DispatchMessageW(&fl_msg);
-       have_message = PeekMessageW(&fl_msg, NULL, 0, 0, PM_REMOVE);
-     }
-@@ -638,6 +648,49 @@
-   }
- }
- 
-+void fl_update_focus(void)
-+{
-+  Fl_Widget *focus;
-+  Fl_Window *win;
-+
-+  get_imm_module();
-+
-+  focus = Fl::grab();
-+  if (!focus)
-+    focus = Fl::focus();
-+  if (!focus)
-+    return;
-+
-+  // Grabs are special in that events are sent to the first
-+  // available window
-+  if (focus == Fl::grab())
-+    win = Fl::first_window();
-+  else {
-+    win = focus->as_window();
-+    if (!win)
-+      win = focus->window();
-+  }
-+
-+  if (!win) {
-+    Fl::warning("Cannot find window for widget receiving focus");
-+    return;
-+  }
-+
-+  // No Win32 window created yet
-+  if (!Fl_X::i(win) || !fl_xid(win))
-+    return;
-+
-+  if (focus->simple_keyboard()) {
-+    use_simple_keyboard = true;
-+    if (flImmGetContext(fl_xid(win)) != 0)
-+      flImmAssociateContextEx(fl_xid(win), 0, 0);
-+  } else {
-+    use_simple_keyboard = false;
-+    if (flImmGetContext(fl_xid(win)) == 0)
-+      flImmAssociateContextEx(fl_xid(win), 0, IACE_DEFAULT);
-+  }
-+}
-+
- HWND fl_capture;
- 
- static int mouse_event(Fl_Window *window, int what, int button,
-@@ -785,6 +838,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
-@@ -846,6 +920,8 @@
-   //fl_msg.pt = ???
-   //fl_msg.lPrivate = ???
- 
-+  MSG fl_orig_msg = fl_msg;
-+
-   Fl_Window *window = fl_find(hWnd);
- 
-   if (window) switch (uMsg) {
-@@ -1025,23 +1101,82 @@
-     if (GetKeyState(VK_SCROLL)) state |= FL_SCROLL_LOCK;
-     Fl::e_state = state;
-     static char buffer[1024];
--    if (uMsg == WM_CHAR || uMsg == WM_SYSCHAR) {
- 
-+    if (use_simple_keyboard) {
-+      BYTE keystate[256];
-+      WCHAR wbuf[8];
-+      int ret;
-+
-+      // I'm not sure if we ever get WM_CHAR (& friends) without an initial
-+      // WM_KEYDOWN (& friends), but if we do then we should not send such
-+      // side band events to simple keyboard widgets.
-+      if ((fl_orig_msg.message != WM_KEYDOWN) &&
-+          (fl_orig_msg.message != WM_SYSKEYDOWN) &&
-+          (fl_orig_msg.message != WM_KEYUP) &&
-+          (fl_orig_msg.message != WM_SYSKEYUP))
-+        break;
-+
-+      GetKeyboardState(keystate);
-+
-+      // Pressing Ctrl wreaks havoc with the symbol lookup, so turn that off.
-+      // But AltGr shows up as Ctrl+Alt in Windows, so keep Ctrl if Alt is
-+      // active.
-+      if (!(keystate[VK_MENU] & 0x80))
-+        keystate[VK_CONTROL] = keystate[VK_LCONTROL] = keystate[VK_RCONTROL] = 0;
-+
-+      // We cannot inspect or modify Windows' internal state of the keyboard
-+      // so we have to try to infer information from ToUnicode() and wedge
-+      // things into a known state.
-+      for (int i = 0;i < 2;i++) {
-+        ret = ToUnicode(fl_orig_msg.wParam, 0, keystate, wbuf,
-+                        sizeof(wbuf)/sizeof(wbuf[0]), 0);
-+
-+        // No symbol for this key (or unexpected length)
-+        if ((ret == 0) || (ret < -1)) {
-+          buffer[0] = 0;
-+          Fl::e_length = 0;
-+          break;
-+        }
-+
-+        // A dead key. Convert this to a Unicode combining character so
-+        // that the application can tell the difference between dead and
-+        // normal keys.
-+        if (ret == -1) {
-+          xchar u = (xchar) msdead2fltk(wbuf[0]);
-+          Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1);
-+          buffer[Fl::e_length] = 0;
-+          break;
-+        }
-+
-+        // If we have two characters (or more) from ToUnicode(), that's
-+        // an invalid sequence. One character chould mean a proper symbol,
-+        // or it could mean a composed one. In both cases we need to call
-+        // ToUnicode() again to get something sane.
-+        if (i == 0)
-+          continue;
-+
-+        // We should now have something sane. Give whatever we have to the
-+        // application.
-+        Fl::e_length = fl_utf8fromwc(buffer, 1024, wbuf, ret);
-+        buffer[Fl::e_length] = 0;
-+      }
-+    } else if (uMsg == WM_CHAR || uMsg == WM_SYSCHAR) {
-       xchar u = (xchar) wParam;
- //    Fl::e_length = fl_unicode2utf(&u, 1, buffer);
-       Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1);
-       buffer[Fl::e_length] = 0;
-+    } else {
-+      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...
--	buffer[0] = Fl::e_keysym-FL_KP;
--	Fl::e_length = 1;
--      } else {
--        // Convert to special keypress...
--	buffer[0] = 0;
--	Fl::e_length = 0;
-+    // The keypad area is a bit odd in that we need to change the keysym
-+    // to properly indicate what the user meant, unlike other keys where
-+    // we normally change the text and keep keysym stable.
-+    if (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last) {
-+      // The initial mapping tables give us a keysym that corresponds to
-+      // numlock being on, so we only do something when it is off.
-+      if (!(state & FL_NUM_LOCK)) {
- 	switch (Fl::e_keysym) {
- 	  case FL_KP + '0' :
- 	    Fl::e_keysym = FL_Insert;
-@@ -1073,30 +1208,10 @@
- 	  case FL_KP + '.' :
- 	    Fl::e_keysym = FL_Delete;
- 	    break;
--	  case FL_KP + '/' :
--	  case FL_KP + '*' :
--	  case FL_KP + '-' :
--	  case FL_KP + '+' :
--	    buffer[0] = Fl::e_keysym-FL_KP;
--	    Fl::e_length = 1;
--	    break;
- 	}
-       }
--    } 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.
-       if (Fl::handle(FL_KEYUP, window)) return 0;
diff --git a/contrib/fltk/05-str2636-fltk-1.3.x-clipboard-x11.patch b/contrib/fltk/02-str2636-fltk-1.3.x-clipboard-x11.patch
similarity index 100%
rename from contrib/fltk/05-str2636-fltk-1.3.x-clipboard-x11.patch
rename to contrib/fltk/02-str2636-fltk-1.3.x-clipboard-x11.patch
diff --git a/contrib/fltk/03-str2599-fltk-1.3.x-keyboard-osx.patch b/contrib/fltk/03-str2599-fltk-1.3.x-keyboard-osx.patch
deleted file mode 100644
index cf13aad..0000000
--- a/contrib/fltk/03-str2599-fltk-1.3.x-keyboard-osx.patch
+++ /dev/null
@@ -1,375 +0,0 @@
-diff -ur fltk-1.3.0r9619.org/configure.in fltk-1.3.0r9619/configure.in
---- fltk-1.3.0r9619.org/configure.in	2012-04-22 04:45:09.000000000 +0200
-+++ fltk-1.3.0r9619/configure.in	2012-06-18 13:47:33.290447462 +0200
-@@ -865,6 +865,8 @@
-     Darwin*)
-         # MacOS X uses Cocoa for graphics.
-         LIBS="$LIBS -framework Cocoa"
-+        # And some Carbon for keyboard handling
-+        LIBS="$LIBS -framework Carbon"
- 
- 	if test x$have_pthread = xyes; then
- 	    AC_DEFINE(HAVE_PTHREAD)
-diff -ur fltk-1.3.0r9619.org/src/Fl_cocoa.mm fltk-1.3.0r9619/src/Fl_cocoa.mm
---- fltk-1.3.0r9619.org/src/Fl_cocoa.mm	2012-06-16 10:49:52.000000000 +0200
-+++ fltk-1.3.0r9619/src/Fl_cocoa.mm	2012-06-18 13:47:42.944910782 +0200
-@@ -53,6 +53,7 @@
- #include <math.h>
- 
- #import <Cocoa/Cocoa.h>
-+#import <Carbon/Carbon.h>
- 
- #ifndef NSINTEGER_DEFINED // appears with 10.5 in NSObjCRuntime.h
- #if defined(__LP64__) && __LP64__
-@@ -114,6 +115,8 @@
- extern Fl_Window* fl_xmousewin;
- #endif
- 
-+bool use_simple_keyboard = false;
-+
- enum { FLTKTimerEvent = 1, FLTKDataReadyEvent };
- 
- 
-@@ -130,6 +133,39 @@
- {
- }
- 
-+// Undocumented voodoo. Taken from Mozilla.
-+#define ENABLE_ROMAN_KYBDS_ONLY -23
-+
-+void fl_update_focus(void)
-+{
-+  Fl_Widget *focus;
-+
-+  focus = Fl::grab();
-+  if (!focus)
-+    focus = Fl::focus();
-+  if (!focus)
-+    return;
-+
-+  if (focus->simple_keyboard())
-+    use_simple_keyboard = true;
-+  else
-+    use_simple_keyboard = false;
-+
-+  // Force a "Roman" or "ASCII" keyboard, which both the Mozilla and
-+  // Safari people seem to think implies turning off advanced IME stuff
-+  // (see nsTSMManager::SyncKeyScript in Mozilla and enableSecureTextInput
-+  // in Safari/Webcore). Should be good enough for us then...
-+#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
-+  CFArrayRef inputSources = TISCreateASCIICapableInputSourceList();
-+  TSMSetDocumentProperty(TSMGetActiveDocument(),
-+                         kTSMDocumentEnabledInputSourcesPropertyTag,
-+                         sizeof(CFArrayRef), &inputSources);
-+  CFRelease(inputSources);
-+#else
-+  KeyScript(use_simple_keyboard ? ENABLE_ROMAN_KYBDS_ONLY : smKeyEnableKybds);
-+#endif  
-+}
-+
- /*
-  * Mac keyboard lookup table
-  */
-@@ -908,6 +944,25 @@
- }
- @end
- 
-+static const char* cocoaDead2FLTK(const char *in)
-+{
-+  if (strcmp(in, "\140") == 0)      // GRAVE ACCENT
-+    return "\314\200";              // COMBINING GRAVE ACCENT
-+  if (strcmp(in, "\302\264") == 0)  // ACUTE ACCENT
-+    return "\314\201";              // COMBINING ACUTE ACCENT
-+  if (strcmp(in, "\136") == 0)      // CIRCUMFLEX ACCENT
-+    return "\314\202";              // COMBINING CIRCUMFLEX ACCENT
-+  if (strcmp(in, "\176") == 0)      // TILDE
-+    return "\314\203";              // COMBINING TILDE
-+  if (strcmp(in, "\302\250") == 0)  // DIAERESIS
-+    return "\314\210";              // COMBINING DIAERESIS
-+  // FIXME: OS X dead key behaviour isn't documented and I don't have
-+  //        any more keyboards to test with...
-+
-+  // hope that OS X gave us something proper to begin with
-+  return in;
-+}
-+
- /*
- Handle cocoa keyboard events
- Events during a character composition sequence:
-@@ -1648,6 +1703,7 @@
- - (void)rightMouseDragged:(NSEvent *)theEvent;
- - (void)otherMouseDragged:(NSEvent *)theEvent;
- - (void)scrollWheel:(NSEvent *)theEvent;
-++ (NSString *)keyTranslate:(UInt16)keyCode withModifierFlags:(UInt32)modifierFlags;
- - (BOOL)handleKeyDown:(NSEvent *)theEvent;
- - (void)keyDown:(NSEvent *)theEvent;
- - (void)keyUp:(NSEvent *)theEvent;
-@@ -1726,6 +1782,130 @@
- - (void)scrollWheel:(NSEvent *)theEvent {
-   cocoaMouseWheelHandler(theEvent);
- }
-++ (NSString *)keyTranslate:(UInt16)keyCode withModifierFlags:(UInt32)modifierFlags {
-+  const UCKeyboardLayout *layout;
-+  OSStatus err;
-+
-+  layout = NULL;
-+
-+#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
-+  TISInputSourceRef keyboard;
-+  CFDataRef uchr;
-+
-+  keyboard = TISCopyCurrentKeyboardInputSource();
-+  uchr = (CFDataRef)TISGetInputSourceProperty(keyboard,
-+                                              kTISPropertyUnicodeKeyLayoutData);
-+  if (uchr == NULL)
-+    return nil;
-+
-+  layout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
-+#else
-+  KeyboardLayoutRef old_layout;
-+  int kind;
-+
-+  err = KLGetCurrentKeyboardLayout(&old_layout);
-+  if (err != noErr)
-+    return nil;
-+
-+  err = KLGetKeyboardLayoutProperty(old_layout, kKLKind,
-+                                    (const void**)&kind);
-+  if (err != noErr)
-+    return nil;
-+
-+  // Old, crufty layout format?
-+  if (kind == kKLKCHRKind) {
-+    void *kchr_layout;
-+
-+    UInt32 chars, state;
-+    char buf[3];
-+
-+    unichar result[16];
-+    ByteCount in_len, out_len;
-+
-+    err = KLGetKeyboardLayoutProperty(old_layout, kKLKCHRData,
-+                                      (const void**)&kchr_layout);
-+    if (err != noErr)
-+      return nil;
-+
-+    state = 0;
-+
-+    keyCode &= 0x7f;
-+    modifierFlags &= 0xff00;
-+
-+    chars = KeyTranslate(kchr_layout, keyCode | modifierFlags, &state);
-+
-+    buf[0] = (chars >> 16) & 0xff;
-+    buf[1] = chars & 0xff;
-+    buf[2] = '\0';
-+
-+    if (buf[0] == '\0') {
-+      buf[0] = buf[1];
-+      buf[1] = '\0';
-+    }
-+
-+    // The data is now in some layout specific encoding. Need to convert
-+    // this to unicode.
-+
-+    ScriptCode script;
-+    TextEncoding encoding;
-+    TECObjectRef converter;
-+
-+    script = (ScriptCode)GetScriptManagerVariable(smKeyScript);
-+
-+    err = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare,
-+                                          kTextRegionDontCare, NULL,
-+                                          &encoding);
-+    if (err != noErr)
-+      return nil;
-+
-+    err = TECCreateConverter(&converter, encoding, kTextEncodingUnicodeV4_0);
-+    if (err != noErr)
-+      return nil;
-+
-+    in_len = strlen(buf);
-+    out_len = sizeof(result);
-+
-+    err = TECConvertText(converter, (ConstTextPtr)buf, in_len, &in_len,
-+                         (TextPtr)result, out_len, &out_len);
-+
-+    TECDisposeConverter(converter);
-+
-+    if (err != noErr)
-+      return nil;
-+
-+    return [NSString stringWithCharacters:result
-+                     length:(out_len / sizeof(unichar))];
-+  }
-+
-+  if ((kind != kKLKCHRuchrKind) && (kind != kKLuchrKind))
-+    return nil;
-+
-+  err = KLGetKeyboardLayoutProperty(old_layout, kKLuchrData,
-+                                    (const void**)&layout);
-+  if (err != noErr)
-+    return nil;
-+#endif
-+ 
-+  if (layout == NULL)
-+    return nil;
-+
-+  UInt32 dead_state;
-+  UniCharCount max_len, actual_len;
-+  UniChar string[255];
-+
-+  dead_state = 0;
-+  max_len = sizeof(string)/sizeof(*string);
-+
-+  modifierFlags = (modifierFlags >> 8) & 0xff;
-+
-+  err = UCKeyTranslate(layout, keyCode, kUCKeyActionDown, modifierFlags,
-+                       LMGetKbdType(), 0, &dead_state, max_len, &actual_len,
-+                       string);
-+  if (err != noErr)
-+    return nil;
-+
-+  return [NSString stringWithCharacters:string length:actual_len];
-+}
- - (BOOL)handleKeyDown:(NSEvent *)theEvent {
-   //NSLog(@"handleKeyDown");
-   fl_lock_function();
-@@ -1752,14 +1932,47 @@
-       break;
-     }
-   }
--  if (!no_text_key && !(Fl::e_state & FL_META) ) {
--    // Don't send cmd-<key> to interpretKeyEvents because it beeps.
-+  if (!no_text_key) {
-+    // The simple keyboard model will ignore insertText, so we need to grab
-+    // the symbol directly from the event. Note that we still use setMarkedText.
-+    if (use_simple_keyboard) {
-+      NSString *simple_chars;
-+      UInt32 modifiers;
-+
-+      // We want a "normal" symbol out of the event, which basically means
-+      // we only respect the shift and alt/altgr modifiers. Cocoa can help
-+      // us if we only wanted shift, but as we also want alt/altgr, we'll
-+      // have to do some lookup ourselves. This matches our behaviour on
-+      // other platforms.
-+
-+      modifiers = 0;
-+      if ([theEvent modifierFlags] & NSAlphaShiftKeyMask)
-+        modifiers |= alphaLock;
-+      if ([theEvent modifierFlags] & NSShiftKeyMask)
-+        modifiers |= shiftKey;
-+      if ([theEvent modifierFlags] & NSAlternateKeyMask)
-+        modifiers |= optionKey;
-+
-+      simple_chars = [FLView keyTranslate:[theEvent keyCode]
-+                             withModifierFlags:modifiers];
-+      if (simple_chars == nil) {
-+        // Something went wrong. Fall back to what Cocoa gave us...
-+        simple_chars = [theEvent charactersIgnoringModifiers];
-+      }
-+
-+      [FLView prepareEtext:simple_chars];
-+    }
-+
-     // Then we can let the OS have a stab at it and see if it thinks it
-     // should result in some text
--    NSText *edit = [[theEvent window]  fieldEditor:YES forObject:nil];
--    in_key_event = true;
--    [edit interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
--    in_key_event = false;
-+
-+    // Don't send cmd-<key> to interpretKeyEvents because it beeps.
-+    if (!(Fl::e_state & FL_META)) {
-+      NSText *edit = [[theEvent window]  fieldEditor:YES forObject:nil];
-+      in_key_event = true;
-+      [edit interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
-+      in_key_event = false;
-+    }
-   }
-   //NSLog(@"to text=%@ l=%d", [NSString stringWithUTF8String:Fl::e_text], Fl::e_length);
-   int handled = Fl::handle(FL_KEYDOWN, window);
-@@ -1937,21 +2150,30 @@
-   //NSLog(@"insertText: received=%@",received);
- 
-   if (!in_key_event) fl_lock_function();
-+
-+  // Simple keyboard widgets do not want these side channel inputs.
-+  if (use_simple_keyboard)
-+    goto end;
-+
-   [FLView prepareEtext:received];
-+
-   // We can get called outside of key events (e.g. from the character
--  // palette). Transform such actions to FL_PASTE events.
-+  // palette). We need to fake our own key event at that point.
-   if (!in_key_event) {
-     Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
--    Fl::handle(FL_PASTE, target);
-+    Fl::e_keysym = Fl::e_original_keysym = 0;
-+    Fl::handle(FL_KEYDOWN, target);
-     // for some reason, the window does not redraw until the next mouse move or button push
-     // sending a 'redraw()' or 'awake()' does not solve the issue!
-     Fl::flush();
-   }
-+
-+end:
-   if (!in_key_event) fl_unlock_function();
- }
- 
- - (void)setMarkedText:(id)aString selectedRange:(NSRange)newSelection  {
--  NSString *received;
-+  NSString *received, *current, *aggregate;
-   if (newSelection.location == 0) {
-     [self unmarkText];
-     return;
-@@ -1962,11 +2184,47 @@
-     received = (NSString*)aString;
-   }
-   //NSLog(@"setMarkedText: %@ %d %d",received,newSelection.location,newSelection.length);
-+
-+  fl_lock_function();
-+
-+  // Simple keyboard widgets generally do not want these side channel
-+  // inputs, but we have no other way of getting dead keys so we make
-+  // an exception in that case.
-+  if (use_simple_keyboard) {
-+    if (in_key_event && (Fl::e_length == 0)) {
-+      [FLView prepareEtext:received];
-+
-+      Fl::e_text = (char*)cocoaDead2FLTK(Fl::e_text);
-+      Fl::e_length = strlen(Fl::e_text);
-+    }
-+    goto end;
-+  }
-+
-   // This code creates the OS X behaviour of seeing dead keys as things
-   // are being composed.
-+  //
-+  // Note: The concatenation thing is because of how OS X deals with
-+  //       invalid sequences. At that point it will spit out one call
-+  //       to insertText with the now aborted sequence, and one new
-+  //       call to setMarkedText with the new sequence. Since we want
-+  //       both to be visible, we need to concatenate.
-   next_compose_length = newSelection.location;
--  [FLView prepareEtext:received];
--  //NSLog(@"Fl::e_text=%@ Fl::e_length=%d next_compose_length=%d", received, Fl::e_length, next_compose_length);
-+  current = [NSString stringWithUTF8String:Fl::e_text];
-+  aggregate = [current stringByAppendingString:received];
-+
-+  [FLView prepareEtext:aggregate];
-+  //NSLog(@"Fl::e_text=%@ Fl::e_length=%d next_compose_length=%d", aggregate, Fl::e_length, next_compose_length);
-+
-+  // We can get called outside of key events (e.g. from the character
-+  // palette). We need to fake our own key event at that point.
-+  if (!in_key_event) {
-+    Fl_Window *target = [(FLWindow*)[self window] getFl_Window];
-+    Fl::e_keysym = Fl::e_original_keysym = 0;
-+    Fl::handle(FL_KEYDOWN, target);
-+  }
-+
-+end:
-+  fl_unlock_function();
- }
- 
- - (void)unmarkText {
diff --git a/contrib/fltk/06-str2636-fltk-1.3.x-clipboard-win32-fix.patch b/contrib/fltk/03-str2636-fltk-1.3.x-clipboard-win32-fix.patch
similarity index 100%
rename from contrib/fltk/06-str2636-fltk-1.3.x-clipboard-win32-fix.patch
rename to contrib/fltk/03-str2636-fltk-1.3.x-clipboard-win32-fix.patch
diff --git a/contrib/fltk/07-str2636-fltk-1.3.x-clipboard-win32.patch b/contrib/fltk/04-str2636-fltk-1.3.x-clipboard-win32.patch
similarity index 100%
rename from contrib/fltk/07-str2636-fltk-1.3.x-clipboard-win32.patch
rename to contrib/fltk/04-str2636-fltk-1.3.x-clipboard-win32.patch
diff --git a/contrib/fltk/08-str2636-fltk-1.3.x-clipboard-osx.patch b/contrib/fltk/05-str2636-fltk-1.3.x-clipboard-osx.patch
similarity index 100%
rename from contrib/fltk/08-str2636-fltk-1.3.x-clipboard-osx.patch
rename to contrib/fltk/05-str2636-fltk-1.3.x-clipboard-osx.patch
diff --git a/contrib/fltk/09-str2659-pixmap.patch b/contrib/fltk/06-str2659-pixmap.patch
similarity index 100%
rename from contrib/fltk/09-str2659-pixmap.patch
rename to contrib/fltk/06-str2659-pixmap.patch
diff --git a/contrib/fltk/10-str2660-fltk-1.3.x-cursor.patch b/contrib/fltk/07-str2660-fltk-1.3.x-cursor.patch
similarity index 100%
rename from contrib/fltk/10-str2660-fltk-1.3.x-cursor.patch
rename to contrib/fltk/07-str2660-fltk-1.3.x-cursor.patch
diff --git a/contrib/fltk/11-str2802-fltk-1.3.0-modal.patch b/contrib/fltk/08-str2802-fltk-1.3.0-modal.patch
similarity index 100%
rename from contrib/fltk/11-str2802-fltk-1.3.0-modal.patch
rename to contrib/fltk/08-str2802-fltk-1.3.0-modal.patch
diff --git a/contrib/fltk/12-str2816-fltk-1.3.0-icons.patch b/contrib/fltk/09-str2816-fltk-1.3.0-icons.patch
similarity index 100%
rename from contrib/fltk/12-str2816-fltk-1.3.0-icons.patch
rename to contrib/fltk/09-str2816-fltk-1.3.0-icons.patch
diff --git a/contrib/fltk/13-str2860-fltk-1.3.x-screen_num.patch b/contrib/fltk/10-str2860-fltk-1.3.x-screen_num.patch
similarity index 100%
rename from contrib/fltk/13-str2860-fltk-1.3.x-screen_num.patch
rename to contrib/fltk/10-str2860-fltk-1.3.x-screen_num.patch
diff --git a/contrib/fltk/14-str2860-fltk-1.3.x-multihead.patch b/contrib/fltk/11-str2860-fltk-1.3.x-multihead.patch
similarity index 100%
rename from contrib/fltk/14-str2860-fltk-1.3.x-multihead.patch
rename to contrib/fltk/11-str2860-fltk-1.3.x-multihead.patch
diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx
index 7d55168..2209fe5 100644
--- a/vncviewer/Viewport.cxx
+++ b/vncviewer/Viewport.cxx
@@ -86,16 +86,14 @@
        ID_CTRL, ID_ALT, ID_MENUKEY, ID_CTRLALTDEL,
        ID_REFRESH, ID_OPTIONS, ID_INFO, ID_ABOUT, ID_DISMISS };
 
+// Fake key presses use this value and above
+static const int fakeKeyBase = 0x200;
+
 Viewport::Viewport(int w, int h, const rfb::PixelFormat& serverPF, CConn* cc_)
   : Fl_Widget(0, 0, w, h), cc(cc_), frameBuffer(NULL),
     lastPointerPos(0, 0), lastButtonMask(0),
     cursor(NULL), menuCtrlKey(false), menuAltKey(false)
 {
-// FLTK STR #2599 must be fixed for proper dead keys support
-#ifdef HAVE_FLTK_DEAD_KEYS
-  set_simple_keyboard();
-#endif
-
 // FLTK STR #2636 gives us the ability to monitor clipboard changes
 #ifdef HAVE_FLTK_CLIPBOARD
   Fl::add_clipboard_notify(handleClipboardChange, this);
@@ -399,26 +397,15 @@
     // Release all keys that were pressed as that generally makes most
     // sense (e.g. Alt+Tab where we only see the Alt press)
     while (!downKeySym.empty())
-      handleKeyEvent(downKeySym.begin()->first, downKeySym.begin()->first,
-                     "", false);
+      handleKeyRelease(downKeySym.begin()->first);
     return 1;
 
   case FL_KEYDOWN:
-    if (menuKeyCode && (Fl::event_key() == menuKeyCode)) {
-      popupContextMenu();
-      return 1;
-    }
-
-    handleKeyEvent(Fl::event_key(), Fl::event_original_key(),
-                   Fl::event_text(), true);
+    handleFLTKKeyPress();
     return 1;
 
   case FL_KEYUP:
-    if (menuKeyCode && (Fl::event_key() == menuKeyCode))
-      return 1;
-
-    handleKeyEvent(Fl::event_key(), Fl::event_original_key(),
-                   Fl::event_text(), false);
+    handleKeyRelease(Fl::event_original_key());
     return 1;
   }
 
@@ -495,9 +482,156 @@
 }
 
 
-rdr::U32 Viewport::translateKeyEvent(int keyCode, int origKeyCode, const char *keyText)
+void Viewport::handleKeyPress(int keyCode, rdr::U32 keySym)
+{
+  static bool menuRecursion = false;
+
+  // Prevent recursion if the menu wants to send its own
+  // activation key.
+  if (menuKeySym && (keySym == menuKeySym) && !menuRecursion) {
+    menuRecursion = true;
+    popupContextMenu();
+    menuRecursion = false;
+    return;
+  }
+
+  if (viewOnly)
+    return;
+
+#ifdef __APPLE__
+  // Alt on OS X behaves more like AltGr on other systems, and to get
+  // sane behaviour we should translate things in that manner for the
+  // remote VNC server. However that means we lose the ability to use
+  // Alt as a shortcut modifier. Do what RealVNC does and hijack the
+  // left command key as an Alt replacement.
+  switch (keySym) {
+  case XK_Super_L:
+    keySym = XK_Alt_L;
+    break;
+  case XK_Super_R:
+    keySym = XK_Super_L;
+    break;
+  case XK_Alt_L:
+  case XK_Alt_R:
+    keySym = XK_ISO_Level3_Shift;
+    break;
+  }
+#endif
+
+#ifdef WIN32
+  // Ugly hack alert!
+  //
+  // Windows doesn't have a proper AltGr, but handles it using fake
+  // Ctrl+Alt. Unfortunately X11 doesn't generally like the combination
+  // Ctrl+Alt+AltGr, which we usually end up with when Xvnc tries to
+  // get everything in the correct state. Cheat and temporarily release
+  // Ctrl and Alt when we send some other symbol.
+  bool ctrlPressed, altPressed;
+  DownMap::iterator iter;
+
+  ctrlPressed = false;
+  altPressed = false;
+  for (iter = downKeySym.begin();iter != downKeySym.end();++iter) {
+    if (iter->second == XK_Control_L)
+      ctrlPressed = true;
+    else if (iter->second == XK_Alt_R)
+      altPressed = true;
+  }
+
+  if (ctrlPressed && altPressed) {
+    vlog.debug("Faking release of AltGr (Ctrl_L+Alt_R)");
+    try {
+      cc->writer()->keyEvent(XK_Control_L, false);
+      cc->writer()->keyEvent(XK_Alt_R, false);
+    } catch (rdr::Exception& e) {
+      vlog.error("%s", e.str());
+      exit_vncviewer(e.str());
+    }
+  }
+#endif
+
+  // Because of the way keyboards work, we cannot expect to have the same
+  // symbol on release as when pressed. This breaks the VNC protocol however,
+  // so we need to keep track of what keysym a key _code_ generated on press
+  // and send the same on release.
+  downKeySym[keyCode] = keySym;
+
+#if defined(WIN32) || defined(__APPLE__)
+  vlog.debug("Key pressed: 0x%04x => 0x%04x", keyCode, keySym);
+#else
+  vlog.debug("Key pressed: 0x%04x => XK_%s (0x%04x)",
+             keyCode, XKeysymToString(keySym), keySym);
+#endif
+
+  try {
+    cc->writer()->keyEvent(keySym, true);
+  } catch (rdr::Exception& e) {
+    vlog.error("%s", e.str());
+    exit_vncviewer(e.str());
+  }
+
+#ifdef WIN32
+  // Ugly hack continued...
+  if (ctrlPressed && altPressed) {
+    vlog.debug("Restoring AltGr state");
+    try {
+      cc->writer()->keyEvent(XK_Control_L, true);
+      cc->writer()->keyEvent(XK_Alt_R, true);
+    } catch (rdr::Exception& e) {
+      vlog.error("%s", e.str());
+      exit_vncviewer(e.str());
+    }
+  }
+#endif
+}
+
+
+void Viewport::handleKeyRelease(int keyCode)
+{
+  DownMap::iterator iter;
+
+  if (viewOnly)
+    return;
+
+  iter = downKeySym.find(keyCode);
+  if (iter == downKeySym.end()) {
+    // These occur somewhat frequently so let's not spam them unless
+    // logging is turned up.
+    vlog.debug("Unexpected release of key code %d", keyCode);
+    return;
+  }
+
+#if defined(WIN32) || defined(__APPLE__)
+  vlog.debug("Key released: 0x%04x => 0x%04x", keyCode, iter->second);
+#else
+  vlog.debug("Key released: 0x%04x => XK_%s (0x%04x)",
+             keyCode, XKeysymToString(iter->second), iter->second);
+#endif
+
+  try {
+    cc->writer()->keyEvent(iter->second, false);
+  } catch (rdr::Exception& e) {
+    vlog.error("%s", e.str());
+    exit_vncviewer(e.str());
+  }
+
+  downKeySym.erase(iter);
+}
+
+
+rdr::U32 Viewport::translateKeyEvent(void)
 {
   unsigned ucs;
+  int keyCode, origKeyCode;
+  const char *keyText;
+  int keyTextLen;
+
+  keyCode = Fl::event_key();
+  origKeyCode = Fl::event_original_key();
+  keyText = Fl::event_text();
+  keyTextLen = Fl::event_length();
+
+  vlog.debug("FLTK key %d (%d) '%s'[%d]", origKeyCode, keyCode, keyText, keyTextLen);
 
   // First check for function keys
   if ((keyCode > FL_F) && (keyCode <= FL_F_Last))
@@ -541,23 +675,6 @@
     }
   }
 
-#ifdef __APPLE__
-  // Alt on OS X behaves more like AltGr on other systems, and to get
-  // sane behaviour we should translate things in that manner for the
-  // remote VNC server. However that means we lose the ability to use
-  // Alt as a shortcut modifier. Do what RealVNC does and hijack the
-  // left command key as an Alt replacement.
-  switch (keyCode) {
-  case FL_Meta_L:
-    return XK_Alt_L;
-  case FL_Meta_R:
-    return XK_Super_L;
-  case FL_Alt_L:
-  case FL_Alt_R:
-    return XK_ISO_Level3_Shift;
-  }
-#endif
-
 #if defined(WIN32) || defined(__APPLE__)
   // X11 fairly consistently uses XK_KP_Separator for comma and
   // XK_KP_Decimal for period. Windows and OS X are a different matter
@@ -722,113 +839,15 @@
 }
 
 
-void Viewport::handleKeyEvent(int keyCode, int origKeyCode, const char *keyText, bool down)
+void Viewport::handleFLTKKeyPress(void)
 {
   rdr::U32 keySym;
 
-  if (viewOnly)
-    return;
-
-  // Because of the way keyboards work, we cannot expect to have the same
-  // symbol on release as when pressed. This breaks the VNC protocol however,
-  // so we need to keep track of what keysym a key _code_ generated on press
-  // and send the same on release.
-  if (!down) {
-    DownMap::iterator iter;
-
-    iter = downKeySym.find(origKeyCode);
-    if (iter == downKeySym.end()) {
-      vlog.error(_("Unexpected release of FLTK key code %d (0x%04x)"),
-                 origKeyCode, origKeyCode);
-      return;
-    }
-
-    vlog.debug("Key released: 0x%04x => 0x%04x", origKeyCode, iter->second);
-
-    try {
-      cc->writer()->keyEvent(iter->second, false);
-    } catch (rdr::Exception& e) {
-      vlog.error("%s", e.str());
-      exit_vncviewer(e.str());
-    }
-
-    downKeySym.erase(iter);
-
-    return;
-  }
-
-  keySym = translateKeyEvent(keyCode, origKeyCode, keyText);
+  keySym = translateKeyEvent();
   if (keySym == NoSymbol)
     return;
 
-#ifdef WIN32
-  // Ugly hack alert!
-  //
-  // Windows doesn't have a proper AltGr, but handles it using fake
-  // Ctrl+Alt. Unfortunately X11 doesn't generally like the combination
-  // Ctrl+Alt+AltGr, which we usually end up with when Xvnc tries to
-  // get everything in the correct state. Cheat and temporarily release
-  // Ctrl and Alt whenever we get a key with a symbol.
-  bool need_cheat = true;
-
-  if (keyText[0] == '\0')
-    need_cheat = false;
-  else if ((downKeySym.find(FL_Control_L) == downKeySym.end()) &&
-           (downKeySym.find(FL_Control_R) == downKeySym.end()))
-    need_cheat = false;
-  else if ((downKeySym.find(FL_Alt_L) == downKeySym.end()) &&
-           (downKeySym.find(FL_Alt_R) == downKeySym.end()))
-    need_cheat = false;
-
-  if (need_cheat) {
-    vlog.debug("Faking release of AltGr (Ctrl+Alt)");
-    try {
-      if (downKeySym.find(FL_Control_L) != downKeySym.end())
-        cc->writer()->keyEvent(XK_Control_L, false);
-      if (downKeySym.find(FL_Control_R) != downKeySym.end())
-        cc->writer()->keyEvent(XK_Control_R, false);
-      if (downKeySym.find(FL_Alt_L) != downKeySym.end())
-        cc->writer()->keyEvent(XK_Alt_L, false);
-      if (downKeySym.find(FL_Alt_R) != downKeySym.end())
-        cc->writer()->keyEvent(XK_Alt_R, false);
-    } catch (rdr::Exception& e) {
-      vlog.error("%s", e.str());
-      exit_vncviewer(e.str());
-    }
-  }
-#endif
-
-  vlog.debug("Key pressed: 0x%04x (0x%04x) '%s' => 0x%04x",
-             origKeyCode, keyCode, keyText, keySym);
-
-  downKeySym[origKeyCode] = keySym;
-
-  try {
-    cc->writer()->keyEvent(keySym, down);
-  } catch (rdr::Exception& e) {
-    vlog.error("%s", e.str());
-    exit_vncviewer(e.str());
-  }
-
-#ifdef WIN32
-  // Ugly hack continued...
-  if (need_cheat) {
-    vlog.debug("Restoring AltGr state");
-    try {
-      if (downKeySym.find(FL_Control_L) != downKeySym.end())
-        cc->writer()->keyEvent(XK_Control_L, true);
-      if (downKeySym.find(FL_Control_R) != downKeySym.end())
-        cc->writer()->keyEvent(XK_Control_R, true);
-      if (downKeySym.find(FL_Alt_L) != downKeySym.end())
-        cc->writer()->keyEvent(XK_Alt_L, true);
-      if (downKeySym.find(FL_Alt_R) != downKeySym.end())
-        cc->writer()->keyEvent(XK_Alt_R, true);
-    } catch (rdr::Exception& e) {
-      vlog.error(e.str());
-      exit_vncviewer(e.str());
-    }
-  }
-#endif
+  handleKeyPress(Fl::event_original_key(), keySym);
 }
 
 
@@ -853,7 +872,7 @@
   contextMenu->add(_("Alt"), 0, NULL, (void*)ID_ALT,
 		   FL_MENU_TOGGLE | (menuAltKey?FL_MENU_VALUE:0));
 
-  if (menuKeyCode) {
+  if (menuKeySym) {
     char sendMenuKey[64];
     snprintf(sendMenuKey, 64, _("Send %s"), (const char *)menuKey);
     contextMenu->add(sendMenuKey, 0, NULL, (void*)ID_MENUKEY, 0);
@@ -922,25 +941,31 @@
     window()->size(w(), h());
     break;
   case ID_CTRL:
-    handleKeyEvent(FL_Control_L, FL_Control_L, "", m->value());
+    if (m->value())
+      handleKeyPress(fakeKeyBase + 0, XK_Control_L);
+    else
+      handleKeyRelease(fakeKeyBase + 0);
     menuCtrlKey = !menuCtrlKey;
     break;
   case ID_ALT:
-    handleKeyEvent(FL_Alt_L, FL_Alt_L, "", m->value());
+    if (m->value())
+      handleKeyPress(fakeKeyBase + 1, XK_Alt_L);
+    else
+      handleKeyRelease(fakeKeyBase + 1);
     menuAltKey = !menuAltKey;
     break;
   case ID_MENUKEY:
-    handleKeyEvent(menuKeyCode, menuKeyCode, "", true);
-    handleKeyEvent(menuKeyCode, menuKeyCode, "", false);
+    handleKeyPress(fakeKeyBase + 2, menuKeySym);
+    handleKeyRelease(fakeKeyBase + 2);
     break;
   case ID_CTRLALTDEL:
-    handleKeyEvent(FL_Control_L, FL_Control_L, "", true);
-    handleKeyEvent(FL_Alt_L, FL_Alt_L, "", true);
-    handleKeyEvent(FL_Delete, FL_Delete, "", true);
+    handleKeyPress(fakeKeyBase + 3, XK_Control_L);
+    handleKeyPress(fakeKeyBase + 4, XK_Alt_L);
+    handleKeyPress(fakeKeyBase + 5, XK_Delete);
 
-    handleKeyEvent(FL_Delete, FL_Delete, "", false);
-    handleKeyEvent(FL_Alt_L, FL_Alt_L, "", false);
-    handleKeyEvent(FL_Control_L, FL_Control_L, "", false);
+    handleKeyRelease(fakeKeyBase + 5);
+    handleKeyRelease(fakeKeyBase + 4);
+    handleKeyRelease(fakeKeyBase + 3);
     break;
   case ID_REFRESH:
     cc->refreshFramebuffer();
@@ -966,7 +991,7 @@
 
 void Viewport::setMenuKey()
 {
-  menuKeyCode = getMenuKeyCode();
+  getMenuKey(&menuKeyCode, &menuKeySym);
 }
 
 
diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h
index 0523cd1..d9eea35 100644
--- a/vncviewer/Viewport.h
+++ b/vncviewer/Viewport.h
@@ -70,8 +70,11 @@
   void handlePointerEvent(const rfb::Point& pos, int buttonMask);
   static void handlePointerTimeout(void *data);
 
-  rdr::U32 translateKeyEvent(int keyCode, int origKeyCode, const char *keyText);
-  void handleKeyEvent(int keyCode, int origKeyCode, const char *keyText, bool down);
+  void handleKeyPress(int keyCode, rdr::U32 keySym);
+  void handleKeyRelease(int keyCode);
+
+  rdr::U32 translateKeyEvent(void);
+  void handleFLTKKeyPress(void);
 
   void initContextMenu();
   void popupContextMenu();
@@ -91,6 +94,7 @@
   typedef std::map<int, rdr::U32> DownMap;
   DownMap downKeySym;
 
+  rdr::U32 menuKeySym;
   int menuKeyCode;
   Fl_Menu_Button *contextMenu;
 
diff --git a/vncviewer/menukey.cxx b/vncviewer/menukey.cxx
index 2e3a568..9c52f5e 100644
--- a/vncviewer/menukey.cxx
+++ b/vncviewer/menukey.cxx
@@ -20,31 +20,37 @@
 #include <string.h>
 #include <FL/Fl.H>
 
+// FLTK can pull in the X11 headers on some systems
+#ifndef XK_VoidSymbol
+#define XK_MISCELLANY
+#include <rfb/keysymdef.h>
+#endif
+
 #include "menukey.h"
 #include "parameters.h"
 
 static const MenuKeySymbol menuSymbols[] = {
-  {"F1", FL_F + 1},
-  {"F2", FL_F + 2},
-  {"F3", FL_F + 3},
-  {"F4", FL_F + 4},
-  {"F5", FL_F + 5},
-  {"F6", FL_F + 6},
-  {"F7", FL_F + 7},
-  {"F8", FL_F + 8},
-  {"F9", FL_F + 9},
-  {"F10", FL_F + 10},
-  {"F11", FL_F + 11},
-  {"F12", FL_F + 12},
-  {"Pause", FL_Pause},
-  {"Print", FL_Print},
-  {"Scroll_Lock", FL_Scroll_Lock},
-  {"Escape", FL_Escape},
-  {"Insert", FL_Insert},
-  {"Delete", FL_Delete},
-  {"Home", FL_Home},
-  {"Page_Up", FL_Page_Up},
-  {"Page_Down", FL_Page_Down},
+  {"F1", FL_F + 1, XK_F1},
+  {"F2", FL_F + 2, XK_F2},
+  {"F3", FL_F + 3, XK_F3},
+  {"F4", FL_F + 4, XK_F4},
+  {"F5", FL_F + 5, XK_F5},
+  {"F6", FL_F + 6, XK_F6},
+  {"F7", FL_F + 7, XK_F7},
+  {"F8", FL_F + 8, XK_F8},
+  {"F9", FL_F + 9, XK_F9},
+  {"F10", FL_F + 10, XK_F10},
+  {"F11", FL_F + 11, XK_F11},
+  {"F12", FL_F + 12, XK_F12},
+  {"Pause", FL_Pause, XK_Pause},
+  {"Print", FL_Print, XK_Print},
+  {"Scroll_Lock", FL_Scroll_Lock, XK_Scroll_Lock},
+  {"Escape", FL_Escape, XK_Escape},
+  {"Insert", FL_Insert, XK_Insert},
+  {"Delete", FL_Delete, XK_Delete},
+  {"Home", FL_Home, XK_Home},
+  {"Page_Up", FL_Page_Up, XK_Page_Up},
+  {"Page_Down", FL_Page_Down, XK_Page_Down},
 };
 
 int getMenuKeySymbolCount()
@@ -57,15 +63,19 @@
   return menuSymbols;
 }
 
-int getMenuKeyCode()
+void getMenuKey(int *keycode, rdr::U32 *keysym)
 {
-    const char *menuKeyStr;
-    int menuKeyCode = 0;
+  const char *menuKeyStr;
 
-    menuKeyStr = menuKey;
-    for(int i = 0; i < getMenuKeySymbolCount(); i++)
-      if (!strcmp(menuSymbols[i].name, menuKeyStr))
-        menuKeyCode = menuSymbols[i].keycode;
+  menuKeyStr = menuKey;
+  for(int i = 0; i < getMenuKeySymbolCount(); i++) {
+    if (!strcmp(menuSymbols[i].name, menuKeyStr)) {
+      *keycode = menuSymbols[i].keycode;
+      *keysym = menuSymbols[i].keysym;
+      return;
+    }
+  }
 
-    return menuKeyCode;
+  *keycode = 0;
+  *keysym = 0;
 }
diff --git a/vncviewer/menukey.h b/vncviewer/menukey.h
index ecb46ed..fcc5135 100644
--- a/vncviewer/menukey.h
+++ b/vncviewer/menukey.h
@@ -18,12 +18,15 @@
 #ifndef __KEYSYM_H__
 #define __KEYSYM_H__
 
+#include <rdr/types.h>
+
 typedef struct {
   const char* name;
   int keycode;
+  rdr::U32 keysym;
 } MenuKeySymbol;
 
-int getMenuKeyCode();
+void getMenuKey(int *keycode, rdr::U32 *keysym);
 int getMenuKeySymbolCount();
 const MenuKeySymbol* getMenuKeySymbols();