uterm_input_uxkb: update leds to match keyboard state
authorRan Benita <ran234@gmail.com>
Fri, 26 Oct 2012 16:14:32 +0000 (18:14 +0200)
committerDavid Herrmann <dh.herrmann@googlemail.com>
Sat, 27 Oct 2012 16:12:59 +0000 (18:12 +0200)
Upon device wakeup or led state changes, we update the keyboard LEDs to
match the new xkb state.

This means that every kmscon instance retains its own LED state, in the
users eyes. In other words, if you had Num Lock set on one kmscon, switched
to an X VT where it's off, and come back, then Num Lock will be set as
when you left. This is what X server, linux VT, etc. do.

Note that since we need to write the LED events to the evdev devices, we
need to open them RDWR. But since we don't really care what happens to
that write(), that's fine.

Also note that this means NumLock is off by default, which might be
annoying. We need to think how to get some 'setleds' or 'numlockx'
equivalent functionality.

Signed-off-by: Ran Benita <ran234@gmail.com>
Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
src/uterm_input.c
src/uterm_input_uxkb.c

index 56d017d..0dbbe70 100644 (file)
@@ -104,7 +104,7 @@ static int input_wake_up_dev(struct uterm_input_dev *dev)
        if (dev->rfd >= 0)
                return 0;
 
-       dev->rfd = open(dev->node, O_CLOEXEC | O_NONBLOCK | O_RDONLY);
+       dev->rfd = open(dev->node, O_CLOEXEC | O_NONBLOCK | O_RDWR);
        if (dev->rfd < 0) {
                log_warn("cannot open device %s (%d): %m", dev->node, errno);
                return -EFAULT;
index 9c93b5d..969072d 100644 (file)
@@ -30,6 +30,7 @@
 #include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 #include <xkbcommon/xkbcommon.h>
 #include "log.h"
 #include "shl_hook.h"
@@ -137,6 +138,35 @@ enum {
        KEY_REPEATED = 2,
 };
 
+static void uxkb_dev_update_keyboard_leds(struct uterm_input_dev *dev)
+{
+       static const struct {
+               int evdev_led;
+               const char *xkb_led;
+       } leds[] = {
+               { LED_NUML, XKB_LED_NAME_NUM },
+               { LED_CAPSL, XKB_LED_NAME_CAPS },
+               { LED_SCROLLL, XKB_LED_NAME_SCROLL },
+       };
+       struct input_event events[sizeof(leds) / sizeof(*leds)];
+       int i;
+
+       if (!(dev->capabilities & UTERM_DEVICE_HAS_LEDS))
+               return;
+
+       memset(events, 0, sizeof(events));
+
+       for (i = 0; i < sizeof(leds) / sizeof(*leds); i++) {
+               events[i].type = EV_LED;
+               events[i].code = leds[i].evdev_led;
+               if (xkb_state_led_name_is_active(dev->state,
+                                               leds[i].xkb_led) > 0)
+                       events[i].value = 1;
+       }
+
+       write(dev->rfd, events, sizeof(events));
+}
+
 static inline int uxkb_dev_resize_event(struct uterm_input_dev *dev, size_t s)
 {
        uint32_t *tmp;
@@ -269,6 +299,7 @@ int uxkb_dev_process(struct uterm_input_dev *dev,
        xkb_keycode_t keycode;
        const xkb_keysym_t *keysyms;
        int num_keysyms, ret;
+       enum xkb_state_component changed;
 
        if (key_state == KEY_REPEATED)
                return -ENOKEY;
@@ -278,10 +309,14 @@ int uxkb_dev_process(struct uterm_input_dev *dev,
 
        num_keysyms = xkb_state_key_get_syms(state, keycode, &keysyms);
 
+       changed = 0;
        if (key_state == KEY_PRESSED)
-               xkb_state_update_key(state, keycode, XKB_KEY_DOWN);
+               changed = xkb_state_update_key(state, keycode, XKB_KEY_DOWN);
        else if (key_state == KEY_RELEASED)
-               xkb_state_update_key(state, keycode, XKB_KEY_UP);
+               changed = xkb_state_update_key(state, keycode, XKB_KEY_UP);
+
+       if (changed & XKB_STATE_LEDS)
+               uxkb_dev_update_keyboard_leds(dev);
 
        if (num_keysyms <= 0)
                return -ENOKEY;
@@ -324,4 +359,6 @@ void uxkb_dev_reset(struct uterm_input_dev *dev)
        }
        xkb_state_unref(dev->state);
        dev->state = state;
+
+       uxkb_dev_update_keyboard_leds(dev);
 }