--- server/vino-input.c.orig	2006-12-28 16:53:31.000000000 -0500
+++ server/vino-input.c	2006-12-28 16:54:47.000000000 -0500
@@ -74,6 +74,16 @@
  * the client side will handle "key repeat", and if we left the key
  * pressed down on the server as well, we'd get double repeating.)
  * 
+ * Two further bits of special behavior are needed for NumLock; first,
+ * the second key level, which is normally "Shift", actually
+ * corresponds to "NumLock" on keypad keys. Second, since the server's
+ * NumLock key could plausibly be locked in either state, we need to
+ * take its state into account when typing keypad keys. If the server
+ * supports XKB, we use the XkbStateNotify event to track NumLock
+ * state changes. If the server doesn't support XKB, we have to
+ * explicitly query the modifier state any time the client types a
+ * keypad key.
+ * 
  * On servers that support XKB, we also have to worry about multiple
  * keyboard layouts. We need to keep track of what keysyms are
  * available in what layouts, and what layout (or "group") the server
@@ -112,14 +122,18 @@
 #include "vino-util.h"
 
 #define VINO_IS_MODIFIER_KEYSYM(k) (((k) >= XK_Shift_L && (k) <= XK_Hyper_R) || \
+                                     (k) == XK_Num_Lock                      || \
                                      (k) == XK_Mode_switch                   || \
                                      (k) == XK_ISO_Level3_Shift)
 
+#define VINO_IS_KEYPAD_KEYSYM(k)    ((k) >= XK_KP_Space && (k) <= XK_KP_Equal)
+
 typedef enum
 {
   VINO_LEFT_SHIFT  = 1 << 0,
   VINO_RIGHT_SHIFT = 1 << 1,
-  VINO_ALT_GR      = 1 << 2
+  VINO_ALT_GR      = 1 << 2,
+  VINO_NUM_LOCK    = 1 << 3,
 } VinoModifierState;
 
 typedef enum
@@ -127,17 +141,20 @@
   VINO_LEVEL_PLAIN        = 0,
   VINO_LEVEL_SHIFT        = 1,
   VINO_LEVEL_ALTGR        = 2,
-  VINO_LEVEL_SHIFT_ALTGR  = 3
+  VINO_LEVEL_SHIFT_ALTGR  = 3,
+  VINO_LEVEL_NUM_LOCK     = 4
 } VinoLevel;
 
-#define VINO_NUM_LEVELS 4
+#define VINO_NUM_LEVELS 4  /* NumLock is special and doesn't count */
 #define VINO_NUM_GROUPS 4
 
 #define VINO_LEVEL(state) ((((state) & (VINO_LEFT_SHIFT | VINO_RIGHT_SHIFT)) ? VINO_LEVEL_SHIFT : 0) | \
-			   (((state) & VINO_ALT_GR) ? VINO_LEVEL_ALTGR : 0))
+			   (((state) & VINO_ALT_GR) ? VINO_LEVEL_ALTGR : 0) | \
+			   (((state) & VINO_NUM_LOCK) ? VINO_LEVEL_NUM_LOCK : 0))
 
 #define VINO_LEVEL_IS_SHIFT(level)    (level & VINO_LEVEL_SHIFT)
 #define VINO_LEVEL_IS_ALTGR(level)    (level & VINO_LEVEL_ALTGR)
+#define VINO_LEVEL_IS_NUM_LOCK(level) (level & VINO_LEVEL_NUM_LOCK)
 
 typedef struct
 {
@@ -158,8 +175,10 @@
   KeyCode            left_shift_keycode;
   KeyCode            right_shift_keycode;
   KeyCode            alt_gr_keycode;
+  KeyCode            num_lock_keycode;
 
   KeySym             alt_gr_keysym;
+  guint              num_lock_mod;
 
 #ifdef HAVE_XKB
   int                xkb_num_groups;
@@ -184,7 +203,8 @@
   int      min_keycodes, max_keycodes;
   int      keysyms_per_keycode;
   KeySym   sym;
-  int      keycode, level;
+  int      keycode, level, i;
+  XModifierKeymap *modmap;
 
   global_input_data.alt_gr_keysym = XK_Mode_switch;
 
@@ -214,12 +234,30 @@
 
 	  /* There's only one group in plain X, so use "binding[0]". */
 	  binding[0].keycode = keycode;
-	  binding[0].level = level;
+	  binding[0].keypad = VINO_IS_KEYPAD_KEYSYM (sym);
+
+	  if (binding[0].keypad && level == VINO_LEVEL_SHIFT)
+	    binding[0].level = VINO_LEVEL_NUM_LOCK;
+	  else
+	    binding[0].level = level;
 
 	  dprintf (INPUT, "\t0x%.2x (%s) -> key %d level %d\n", (guint)sym,
 		   XKeysymToString (sym), keycode, level);
 	}
     }
+
+  /* Find the modifier mask corresponding to NumLock */
+  keycode = XKeysymToKeycode (xdisplay, XK_Num_Lock);
+  modmap = XGetModifierMapping (xdisplay);
+  for (i = 0; i < 8 * modmap->max_keypermod; i++)
+    {
+      if (modmap->modifiermap[i] == keycode)
+	{
+	  global_input_data.num_lock_mod = 1 << (i / modmap->max_keypermod);
+	  break;
+	}
+    }
+  XFreeModifiermap (modmap);
 }
 
 #ifdef HAVE_XKB
@@ -227,6 +265,7 @@
 vino_input_initialize_keycodes_xkb (Display *xdisplay)
 {
   XkbDescPtr xkb;
+  XkbStateRec state;
   int levelmap[XkbMaxKeyTypes][VINO_NUM_LEVELS], kc, kt, level, group;
   int LevelThreeMask;
   VinoKeybinding *binding;
@@ -328,7 +367,11 @@
 	      if (!binding[group].keycode)
 		{
 		  binding[group].keycode = kc;
-		  binding[group].level = level;
+		  binding[group].keypad = (kt == XkbKeypadIndex);
+		  if (binding[group].keypad && level == VINO_LEVEL_SHIFT)
+		    binding[group].level = VINO_LEVEL_NUM_LOCK;
+		  else
+		    binding[group].level = level;
 		}
 
 	      if (kgroup == group)
@@ -360,6 +403,14 @@
 	}
     }
 
+  /* Find NumLock modifier mask and get initial NumLock state */
+  global_input_data.num_lock_mod = XkbKeysymToModifiers (xdisplay, XK_Num_Lock);
+  if (XkbGetState (xdisplay, XkbUseCoreKbd, &state) == Success)
+    {
+      if (state.locked_mods & global_input_data.num_lock_mod)
+	global_input_data.modifier_state |= VINO_NUM_LOCK;
+    }
+
   XkbFreeKeyboard (xkb, 0, True);
 }
 
@@ -381,6 +432,15 @@
 	      data->current_group = state->group;
 	      dprintf (INPUT, "current_group -> %d\n", data->current_group);
 	    }
+	  if (state->changed & XkbModifierLockMask)
+	    {
+	      if (state->locked_mods & data->num_lock_mod)
+		data->modifier_state |= VINO_NUM_LOCK;
+	      else
+		data->modifier_state &= ~VINO_NUM_LOCK;
+	      dprintf (INPUT, "locked_mods -> %u, modifier_state -> %u\n",
+		       state->locked_mods, data->modifier_state);
+	    }
 	  break;
 
 	case XkbMapNotify:
@@ -416,6 +476,7 @@
 
   global_input_data.left_shift_keycode   = XKeysymToKeycode (xdisplay, XK_Shift_L);
   global_input_data.right_shift_keycode  = XKeysymToKeycode (xdisplay, XK_Shift_R);
+  global_input_data.num_lock_keycode     = XKeysymToKeycode (xdisplay, XK_Num_Lock);
   global_input_data.alt_gr_keycode       = XKeysymToKeycode (xdisplay, global_input_data.alt_gr_keysym);
 }
 #endif /* HAVE_XTEST */
@@ -450,7 +511,8 @@
       global_input_data.current_group = state.group;
 
       XkbSelectEventDetails (xdisplay, XkbUseCoreKbd, XkbStateNotify,
-			     XkbGroupStateMask, XkbGroupStateMask);
+			     XkbGroupStateMask | XkbModifierLockMask,
+			     XkbGroupStateMask | XkbModifierLockMask);
       XkbSelectEventDetails (xdisplay, XkbUseCoreKbd, XkbMapNotify,
 			     XkbAllClientInfoMask, XkbAllClientInfoMask);
       XkbSelectEventDetails (xdisplay, XkbUseCoreKbd, XkbControlsNotify,
@@ -542,6 +604,20 @@
   VinoModifierState modifier_state = input_data->modifier_state;
   int cur_level;
 
+  if (binding->keypad && !input_data->xkb_supported)
+    {
+      Window root, child;
+      int rx, ry, wx, wy;
+      unsigned int mask;
+
+      XQueryPointer (xdisplay, RootWindow (xdisplay, DefaultScreen (xdisplay)),
+		     &root, &child, &rx, &ry, &wx, &wy, &mask);
+      if (mask & input_data->num_lock_mod)
+	modifier_state |= VINO_NUM_LOCK;
+      else
+	modifier_state &= ~VINO_NUM_LOCK;
+    }
+
   cur_level = VINO_LEVEL (modifier_state);
 
   if (VINO_LEVEL_IS_SHIFT (cur_level) && !VINO_LEVEL_IS_SHIFT (binding->level))
@@ -582,6 +658,20 @@
 			 want_pressed,
 			 CurrentTime);
     }
+
+  if (VINO_LEVEL_IS_NUM_LOCK (cur_level) != VINO_LEVEL_IS_NUM_LOCK (binding->level))
+    {
+      dprintf (INPUT, "Faking NumLock press/release\n");
+
+      XTestFakeKeyEvent (xdisplay,
+			 input_data->num_lock_keycode,
+			 True,
+			 CurrentTime);
+      XTestFakeKeyEvent (xdisplay,
+			 input_data->num_lock_keycode,
+			 False,
+			 CurrentTime);
+    }
 }
 
 static gboolean
@@ -678,7 +768,7 @@
     {
       vino_input_fake_keypress (xdisplay, keysym);
     }
-  else if (keysym != XK_Caps_Lock)
+  else if (keysym != XK_Caps_Lock && keysym != XK_Num_Lock)
     {
       KeyCode keycode;
 
