You are not logged in.

#1 2009-11-06 11:02:27

chpln
Member
From: Australia
Registered: 2009-09-17
Posts: 361

[SOLVED] Two keyboards; two keymaps

I'm looking at a replacing my current keyboard with something a bit more comfortable, which has made me wonder whether it's possible modify the keymap of a USB keyboard without affecting the layout on my laptop keyboard and vise versa.

To be a bit more specific, I'm looking at a using a HHKB (which has a slightly unusual layout) along with the keyboard in my Thinkpad.  Ideally, I'd like to keep the physical layout of these keyboards as similar as possible to each other.  For me this means exchanging Backslash / Bar and Backspace on my laptop keyboard, while leaving them intact on the HHKB, and swapping Backtick / Tilde and Escape on the HHKB, while leaving them unchanged on the laptop.

In other words, I wish to use a tool such as xmodmap to tailor two keyboard layouts separately; I would rather avoid making modifications to the keyboards themselves.

So, is this possible (udev / hal?), and if so, any pointers on where I should start?

Alternatively, I've seen devices which translate keycodes on-the-fly.  Is anyone able to share their experience with these?

Last edited by chpln (2010-02-01 01:53:51)

Offline

#2 2010-01-31 07:39:03

chpln
Member
From: Australia
Registered: 2009-09-17
Posts: 361

Re: [SOLVED] Two keyboards; two keymaps

I've made progress on this, so I'll post an update for anyone interested.

While I wasn't successful in mapping layouts to individual devices, I was able to have the keymap updated depending on the device connected.  The primary use-case which comes to mind is netbooks / notebooks with an external keyboard, though it should work equally well for any system where a USB device (not necessarily a keyboard) is to be connected to change the keymap.

To achieve this, in short: I created a script to manage keymap changes, configured udev, pm-utils, rc.local and .xinitrc to update the keymap as necessary and removed hal.

First, the script I have been using:

keymap-update.sh

#!/bin/bash
# keymap-update.sh --- Update the keymap accordingly
#
# Copyright (C) 2009, 2010 Chris Mann
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

. /etc/rc.conf
. /etc/rc.d/functions

export DISPLAY=':0.0'
XMODMAP_COMMAND="/usr/bin/xmodmap"
XMODMAP_ARGS=""
CONSOLE_COMMAND="/bin/loadkeys"
CONSOLE_ARGS="-q -u"

. /etc/keymaprc

set_key_map () {
    if [ $SETSTD -eq 0 -a $SETALT -eq 0 ]; then
        /usr/sbin/lsusb | grep "ID $VENDOR:$PRODUCT" &> /dev/null && \
            SETALT=1 || SETSTD=1
    fi

    test ${XMODMAP} -eq 1 && \
        update_map "${XMODMAP_COMMAND}" "${XMODMAP_STD}" "${XMODMAP_ALT}" \
        ${XMODMAP_ARGS}
    test ${CONSOLEMAP} -eq 1 && \
        update_map "${CONSOLE_COMMAND}" "${CONSOLE_STD}" "${CONSOLE_ALT}" \
        ${CONSOLE_ARGS}
    return 0
}

update_map () {
    COMMAND="$1"
    STDMAP="$2"
    ALTMAP="$3"
    shift 3
    ARGS="$@"

    test ${SETSTD} -eq 1 && MAP="${STDMAP}"
    test ${SETALT} -eq 1 && MAP="${ALTMAP}"

    stat_busy "Loading $MAP..."
    "${COMMAND}" ${ARGS} "${MAP}" && stat_done || stat_fail
}

usage () {
    echo "Usage: $0 [-a|-s] [options]
  -a -- Use the alternate keymap
  -s -- Use the standard keymap
  -x -- Update X keymap
  -c -- Update tty keymaps
  -q -- Quiet; suppress output

  If neither -a or -s are specified, connected USB devices will be checked \
for a matching \$VENDOR and \$PRODUCT ID.  If one is found, \$ALTMAP will be \
used."
}

XMODMAP=0
CONSOLEMAP=0
QUIET=0
SETSTD=0
SETALT=0

case "$ACTION" in
    "add") SETALT=1;;
    "remove") SETSTD=1;;
esac

while getopts ":ascxqh" opt; do
    case $opt in
        a) SETALT=1;;
        s) SETSTD=1;;
        c) CONSOLEMAP=1;;
        x) XMODMAP=1;;
        q) QUIET=1;;
        h) usage; exit 0;;
    esac
    shift $(($OPTIND - 1))
done

test $XMODMAP -eq 0 -a $CONSOLEMAP -eq 0 && XMODMAP=1 && CONSOLEMAP=1

test ${SETSTD} -eq 1 && \
    test ${SETALT} -eq 1 && usage && exit 1

if [ ${QUIET} -eq 1 ]; then
    set_key_map "$1" &> /dev/null
else
    set_key_map "$1"
fi

I'll assume that xmodmap and/or console maps for each layout have already been created.  Since there are plenty of resources for creating these, I won't go into any detail about them here.

Next, I grabbed the vendor and product ID for the keyboard (via lsusb) and configured the above script via /etc/keymaprc.  The vendor and product IDs as well as the console and xmodmap layouts should be replaced to suit:

VENDOR=<VENDOR_ID>
PRODUCT=<PRODUCT_ID>

TTYS='/dev/tty1,/dev/tty2,/dev/tty3,/dev/tty4,/dev/tty5,/dev/tty6'

XMODMAP_STD=/PATH/TO/STANDARD/XMODMAPRC
XMODMAP_ALT=/PATH/TO/ALTERNATE/XMODMAPRC

CONSOLE_ARGS="-q -u -C ${TTYS}"
CONSOLE_STD=<STANDARD_CONSOLE_MAP>
CONSOLE_ALT=<ALTERNATE_CONSOLE_MAP>

Now configure udev to run the script depending on the connection state of the keyboard.  Once again, changing this to suit:

/etc/udev/rules.d/98-kbd.rules

ACTION=="add", SUBSYSTEM=="usb", ATTRS{idVendor}=="<VENDOR_ID>", ATTRS{idProduct}=="<PRODUCT_ID>", ENV{REMOVE_CMD}="/path/to/keymap-update -sxc", RUN+="/path/to/keymap-update -axc"

I suspect udev may not be able to detect changes made to device states while the system is suspended (though I've not actually bothered to test).  So, I created the following to update the keymap on resume (this should be made executable):

/etc/udev/sleep.d/90-kbd:

#!/bin/bash

case $1 in
    thaw)
        /path/to/keymap-update -xcq
        ;;
    resume)
        /path/to/keymap-update -xcq
        ;;
esac

And update the keymap following boot and xinit:

/etc/rc.local:

...
/path/to/keymap-update -cq &
...

~/.xinitrc

...
/path/to/keymap-update -xq &
...

The final and probably most time-consuming part is removing hal from the system.  The reason for this is hal will often override the specified keymap entirely when the change of device takes place.  I won't go into detail removing hal (unless someone would like some more information).  For me, it was simply a matter of rebuilding half-a-dozen packages with hal support disabled and configuring xorg.conf.

The end result is a tailored layout in X and/or the console depending on whether a nominated USB device is present.

Last edited by chpln (2010-01-31 07:43:07)

Offline

#3 2011-06-13 21:07:32

aragats
Member
From: Broomfield, CO
Registered: 2009-05-26
Posts: 30

Re: [SOLVED] Two keyboards; two keymaps

I have a similar, but not exactly the same problem. I use 3 different languages/layouts. When I plug in an external keyboard I have to run 2 commands:

  setxkbmap -layout us,ru,am -variant ",phonetic,phonetic"
  xmodmap /home/sergeym/.Xmodmap

So I've added them to a script which is called from a udev rule. The script itself works fine, but when it's called from udev it doesn't, it runs, but has no effect at all - does not change layouts and mappings...

Offline

Board footer

Powered by FluxBB