linux-input.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Benjamin Tissoires <benjamin.tissoires@redhat.com>
To: Dmitry Torokhov <dmitry.torokhov@gmail.com>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Jiri Slaby <jslaby@suse.com>,
	Samuel Thibault <samuel.thibault@ens-lyon.org>
Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH 2/4] tty/vt/keyboard: Fix Caps Lock LED on major distributions
Date: Fri, 27 Jan 2017 18:13:16 +0100	[thread overview]
Message-ID: <20170127171318.2596-3-benjamin.tissoires@redhat.com> (raw)
In-Reply-To: <20170127171318.2596-1-benjamin.tissoires@redhat.com>

The way the kernel handles caps lock on TTYs is not compatible with
all keymaps[1].
The solution that was found was to replace the native Caps_Lock by an
other modifier - ControlL_Lock - from user space. ckbcomp generates
a keymap which can emulate the Caps Lock behavior without relying on
the kernel to translate keys into upper case.

On Fedora and RHEL, the generic keymaps are generated by ckbcomp, and
when systemd-localed loads the locale, it calls "loadkeys -u us-intl",
which uses these generated keymaps.

The problem with these custom keymaps is that the Caps Lock LED is now
not updated properly. In the INPUT_LEDS version, the trigger that can
now handle the Caps Lock LED is "kbd-ctrlllock", but the default is
"kbd-capslock".
We can detect such custom keymaps when they are set by the console ioctl,
and in that case, we can sync both triggers "kbd-capslock" and
"kbd-ctrlllock" to restore a proper LED behavior.

Both version of LEDs (with or without INPUT_LEDS) are fixed.

[1] https://bugzilla.kernel.org/show_bug.cgi?id=7746#c24

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
---
 drivers/tty/vt/keyboard.c | 43 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 40 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index 4a3907e..ca1d614 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -65,6 +65,8 @@ static inline int kbd_defleds(void)
 
 #define KBD_DEFLOCK 0
 
+#define KBD_LOCKSTATE_OFFSET 8
+
 /*
  * Handler Tables.
  */
@@ -132,6 +134,8 @@ static int shift_state = 0;
 
 static unsigned int ledstate = -1U;			/* undefined */
 static unsigned char ledioctl;
+static bool caps_as_controlllock;
+static bool task_caps_as_controlllock;
 
 /*
  * Notifier list for console keyboard events
@@ -979,7 +983,7 @@ static void kbd_led_trigger_activate(struct led_classdev *cdev)
 	}
 
 #define KBD_LOCKSTATE_TRIGGER(_led_bit, _name)		\
-	KBD_LED_TRIGGER((_led_bit) + 8, _name)
+	KBD_LED_TRIGGER((_led_bit) + KBD_LOCKSTATE_OFFSET, _name)
 
 static struct kbd_led_trigger kbd_led_triggers[] = {
 	KBD_LED_TRIGGER(VC_SCROLLOCK, "kbd-scrolllock"),
@@ -1004,6 +1008,18 @@ static void kbd_propagate_led_state(unsigned int old_state,
 	unsigned int changed = old_state ^ new_state;
 	int i;
 
+	/*
+	 * special case where user space uses its own caps lock implementation
+	 * through ControlL_Lock.
+	 */
+	if (task_caps_as_controlllock) {
+		trigger = &kbd_led_triggers[VC_CAPSLOCK];
+		trigger->mask |= BIT(VC_CTRLLLOCK) << KBD_LOCKSTATE_OFFSET;
+	} else {
+		trigger = &kbd_led_triggers[VC_CAPSLOCK];
+		trigger->mask &= ~(BIT(VC_CTRLLLOCK) << KBD_LOCKSTATE_OFFSET);
+	}
+
 	for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); i++) {
 		trigger = &kbd_led_triggers[i];
 
@@ -1042,11 +1058,19 @@ static void kbd_init_leds(void)
 static int kbd_update_leds_helper(struct input_handle *handle, void *data)
 {
 	unsigned int leds = *(unsigned int *)data;
+	unsigned int capsl_mask = BIT(VC_CAPSLOCK);
+
+	/*
+	 * special case where user space uses its own caps lock implementation
+	 * through ControlL_Lock.
+	 */
+	if (task_caps_as_controlllock)
+		capsl_mask |= BIT(VC_CTRLLLOCK) << KBD_LOCKSTATE_OFFSET;
 
 	if (test_bit(EV_LED, handle->dev->evbit)) {
 		input_inject_event(handle, EV_LED, LED_SCROLLL, !!(leds & BIT(VC_SCROLLOCK)));
 		input_inject_event(handle, EV_LED, LED_NUML,    !!(leds & BIT(VC_NUMLOCK)));
-		input_inject_event(handle, EV_LED, LED_CAPSL,   !!(leds & BIT(VC_CAPSLOCK)));
+		input_inject_event(handle, EV_LED, LED_CAPSL,   !!(leds & capsl_mask));
 		input_inject_event(handle, EV_SYN, SYN_REPORT, 0);
 	}
 
@@ -1188,7 +1212,8 @@ static void kbd_bh(unsigned long dummy)
 
 	spin_lock_irqsave(&led_lock, flags);
 	leds = getleds();
-	leds |= (unsigned int)kbd->lockstate << 8;
+	leds |= (unsigned int)kbd->lockstate << KBD_LOCKSTATE_OFFSET;
+	task_caps_as_controlllock = caps_as_controlllock;
 	spin_unlock_irqrestore(&led_lock, flags);
 
 	if (leds != ledstate) {
@@ -1939,6 +1964,18 @@ int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm,
 			spin_unlock_irqrestore(&kbd_event_lock, flags);
 			return -EPERM;
 		}
+
+		/*
+		 * See https://bugzilla.kernel.org/show_bug.cgi?id=7746#c24
+		 *
+		 * The kernel can't properly deal with all keymaps/capslock
+		 * interactions, so userspace (ckbcomp) may use ControlL_Lock
+		 * as a replacement for Caps_Lock.
+		 * It works but breaks the LED, so mark it there that we need
+		 * to forward proper Caps Lock LED on K_CTRLLLOCK.
+		 */
+		if (i == KEY_CAPSLOCK)
+			caps_as_controlllock = (v == K_CTRLLLOCK);
 		key_map[i] = U(v);
 		if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
 			do_compute_shiftstate();
-- 
2.9.3

  parent reply	other threads:[~2017-01-27 17:13 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-01-27 17:13 [PATCH 0/4] TTY: fix Caps Lock LED Benjamin Tissoires
2017-01-27 17:13 ` [PATCH 1/4] tty/vt/keyboard: use defined macros for masks Benjamin Tissoires
2017-01-27 17:18   ` Samuel Thibault
2017-01-27 17:13 ` Benjamin Tissoires [this message]
2017-01-27 17:25   ` [PATCH 2/4] tty/vt/keyboard: Fix Caps Lock LED on major distributions Samuel Thibault
2017-01-27 17:29     ` Dmitry Torokhov
2017-01-27 17:13 ` [PATCH 3/4] tty/vt/keyboard: reset the LEDs state at each console change Benjamin Tissoires
2017-01-27 17:31   ` Samuel Thibault
2017-01-27 18:18     ` Benjamin Tissoires
2017-01-27 17:13 ` [PATCH 4/4] Input: leds - force the LED status after .probe() Benjamin Tissoires
2017-01-27 17:34   ` Samuel Thibault
2017-01-27 18:19     ` Benjamin Tissoires
2017-01-27 17:23 ` [PATCH 0/4] TTY: fix Caps Lock LED Samuel Thibault
2017-01-27 18:34   ` Benjamin Tissoires
2017-01-27 18:47     ` Samuel Thibault

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170127171318.2596-3-benjamin.tissoires@redhat.com \
    --to=benjamin.tissoires@redhat.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=jslaby@suse.com \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=samuel.thibault@ens-lyon.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).