Blob Blame History Raw
From 0d2fea8a8ce5e1d2693c25dbeff00461c72427a2 Mon Sep 17 00:00:00 2001
From: Peter Hutterer <peter.hutterer@who-t.net>
Date: Fri, 29 Apr 2016 15:56:03 +1000
Subject: [PATCH xserver] xkb: after changing the keymap, force an indicator
 update

When NumLock is on and a new keymap is applied, the next modifier state change
will turn off that LED (but leave the state enabled). The cause for this is a
bit convoluted:
* the SLI explicitState is copied from the current state in
  ProcXkbGetKbdByName. Thus, if NumLock is on, that state is 0x2.
* on the next modifier key press (e.g. Shift), XkbApplyState() calls into
  XkbUpdateIndicators() -> XkbUpdateLedAutoState() to update SLIs (if any) for
  the currently changed modifier. But it does so with a mask only for the
  changed modifier (i.e. for Shift).
* XkbUpdateLedAutoState() calculates the state based on this mask and
  ends up with 0 because we don't have a Shift LED and we masked out the
  others.
* XkbUpdateLedAutoState() compares that state with the previous state (which
  is still 0x2) and then proceeds to turn the LED off

This doesn't happen in the normal case because either the mask encompasses all
modifiers or the state matches of the masked-out modifiers matches the old
state.

Avoid this issue by forcing an SLI update after changing the keymap. This
updates the sli->effectiveState and thus restores everything to happy working
order.

https://bugzilla.redhat.com/show_bug.cgi?id=1047151

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
---
 include/xkbsrv.h | 4 ++++
 xkb/xkb.c        | 3 ++-
 xkb/xkbLEDs.c    | 2 +-
 3 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/include/xkbsrv.h b/include/xkbsrv.h
index cc6307a..7e71089 100644
--- a/include/xkbsrv.h
+++ b/include/xkbsrv.h
@@ -496,6 +496,10 @@ extern _X_EXPORT void XkbUpdateIndicators(DeviceIntPtr /* keybd */ ,
                                           XkbEventCausePtr      /* cause */
     );
 
+extern _X_EXPORT void XkbUpdateAllDeviceIndicators(XkbChangesPtr /* changes */,
+                                                   XkbEventCausePtr /* cause */
+    );
+
 extern _X_EXPORT XkbSrvLedInfoPtr XkbAllocSrvLedInfo(DeviceIntPtr /* dev */ ,
                                                      KbdFeedbackPtr /* kf */ ,
                                                      LedFeedbackPtr /* lf */ ,
diff --git a/xkb/xkb.c b/xkb/xkb.c
index 294cdf8..3a6ad65 100644
--- a/xkb/xkb.c
+++ b/xkb/xkb.c
@@ -5692,7 +5692,6 @@ ProcXkbListComponents(ClientPtr client)
 }
 
 /***====================================================================***/
-
 int
 ProcXkbGetKbdByName(ClientPtr client)
 {
@@ -6017,6 +6016,8 @@ ProcXkbGetKbdByName(ClientPtr client)
         new = NULL;
     }
     XkbFreeComponentNames(&names, FALSE);
+    XkbUpdateAllDeviceIndicators(NULL, NULL);
+
     return Success;
 }
 
diff --git a/xkb/xkbLEDs.c b/xkb/xkbLEDs.c
index 4e16002..5792d9f 100644
--- a/xkb/xkbLEDs.c
+++ b/xkb/xkbLEDs.c
@@ -304,7 +304,7 @@ XkbUpdateLedAutoState(DeviceIntPtr dev,
     return;
 }
 
-static void
+void
 XkbUpdateAllDeviceIndicators(XkbChangesPtr changes, XkbEventCausePtr cause)
 {
     DeviceIntPtr edev;
-- 
2.7.4