commit 639b85bae81f2272f64a0627d15c80340f6640c0
Author: Andrew Potter <agpotter@gmail.com>
Date:   Wed Oct 24 01:05:50 2012 -0700

    keyboard: Prevent potential infinite loop
    
    Ensure we don't set the numlock setting in response to our own
    numlock-setting event.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=679151

diff --git a/plugins/keyboard/gsd-keyboard-manager.c b/plugins/keyboard/gsd-keyboard-manager.c
index 71c34ad..e8b9b74 100644
--- a/plugins/keyboard/gsd-keyboard-manager.c
+++ b/plugins/keyboard/gsd-keyboard-manager.c
@@ -106,6 +106,7 @@ struct GsdKeyboardManagerPrivate
 #endif
         gint       xkb_event_base;
         GsdNumLockState old_state;
+        gulong     numlock_serial;
         GdkDeviceManager *device_manager;
         guint device_added_id;
         guint device_removed_id;
@@ -666,13 +667,16 @@ numlock_NumLock_modifier_mask (void)
 }
 
 static void
-numlock_set_xkb_state (GsdNumLockState new_state)
+numlock_set_xkb_state (GsdKeyboardManager *manager)
 {
         unsigned int num_mask;
         Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+        GsdNumLockState new_state = manager->priv->old_state;
+
         if (new_state != GSD_NUM_LOCK_STATE_ON && new_state != GSD_NUM_LOCK_STATE_OFF)
                 return;
         num_mask = numlock_NumLock_modifier_mask ();
+        manager->priv->numlock_serial = NextRequest(dpy);
         XkbLockModifiers (dpy, XkbUseCoreKbd, num_mask, new_state == GSD_NUM_LOCK_STATE_ON ? num_mask : 0);
 }
 
@@ -707,18 +711,27 @@ xkb_events_filter (GdkXEvent *xev_,
 	if (xkbev->state.changed & XkbModifierLockMask) {
 		unsigned num_mask = numlock_NumLock_modifier_mask ();
 		unsigned locked_mods = xkbev->state.locked_mods;
+                unsigned long serial = xkbev->state.serial;
 		GsdNumLockState numlock_state;
 
 		numlock_state = (num_mask & locked_mods) ? GSD_NUM_LOCK_STATE_ON : GSD_NUM_LOCK_STATE_OFF;
 
 		if (numlock_state != manager->priv->old_state) {
-			g_debug ("New num-lock state '%s' != Old num-lock state '%s'",
-				 num_lock_state_to_string (numlock_state),
-				 num_lock_state_to_string (manager->priv->old_state));
-			g_settings_set_enum (manager->priv->settings,
-					     KEY_NUMLOCK_STATE,
-					     numlock_state);
-			manager->priv->old_state = numlock_state;
+                        if ( serial > manager->priv->numlock_serial) {
+                                g_debug ("New num-lock state '%s' != Old num-lock state '%s'",
+                                         num_lock_state_to_string (numlock_state),
+                                         num_lock_state_to_string (manager->priv->old_state));
+                                g_settings_set_enum (manager->priv->settings,
+                                                     KEY_NUMLOCK_STATE,
+                                                     numlock_state);
+                                manager->priv->old_state = numlock_state;
+                        } else {
+                                g_debug("Gotcha. Numlock state was changed to a conflicting "
+                                        "value before our own request was processed. This "
+                                        "would have triggered an infinite loop. Please report"
+                                        " this message to "
+                                        "https://bugzilla.gnome.org/show_bug.cgi?id=679151");
+                        }
 		}
 	}
 
@@ -1208,7 +1221,7 @@ apply_numlock (GsdKeyboardManager *manager)
         if (rnumlock) {
                 g_debug ("Remember num-lock is set, so applying setting '%s'",
                          num_lock_state_to_string (manager->priv->old_state));
-                numlock_set_xkb_state (manager->priv->old_state);
+                numlock_set_xkb_state (manager);
         }
 
         XSync (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), FALSE);
@@ -1667,6 +1680,7 @@ static void
 gsd_keyboard_manager_init (GsdKeyboardManager *manager)
 {
         manager->priv = GSD_KEYBOARD_MANAGER_GET_PRIVATE (manager);
+        manager->priv->numlock_serial = G_MAXULONG;
 }
 
 static void
