追記(2011年2月21日)

定家さんこの記事のコードの改良版をgitに公開(下のURL)してくださいました。 http://gitorious.org/at-home-modifier しっかりとしたREADMEなどが付随しているので、現状ではそちらのコードを使用したほうが良いでしょう。 (追記おわり)

xorg 1.8 にアップデートしたとき,evdevも 2.3.3から2.4.0にアップデートした. その関係で, Linux で SandS の実現を目指す : xf86-input-evdev の修正(メモ) – 落書き、時々落学で作ったパッチがそのままでは使えなくなった. そこで,2.4.0用に作りなおした. http://dl.dropbox.com/u/662567/xf86-input-evdev-2.4.0-mad-key.patch 以下のPKGBUILDと一緒に使えば簡単にバイナリができる.

pkgname=xf86-input-evdev
pkgver=2.4.0
pkgrel=1
pkgdesc="X.org evdev input driver"
arch=(i686 x86_64)
url="http://xorg.freedesktop.org/"
license=('custom')
depends=('glibc')
makedepends=('pkgconfig' 'xorg-server>=1.8.0' 'inputproto>=2.0' 'randrproto>=1.3.1')
conflicts=('xorg-server<1.8.0')
options=('!libtool')
groups=('xorg-input-drivers')
source=(${url}/releases/individual/driver/${pkgname}-${pkgver}.tar.bz2
${pkgname}-${pkgver}-mad-key.patch)
build() {
cd "${srcdir}/${pkgname}-${pkgver}"
patch -p0 -i ${srcdir}/${pkgname}-${pkgver}-mad-key.patch  || return 1
./configure --prefix=/usr || return 1
make || return 1
make DESTDIR="${pkgdir}" install || return 1
install -m755 -d "${pkgdir}/usr/share/licenses/${pkgname}"
install -m644 COPYING "${pkgdir}/usr/share/licenses/${pkgname}/" || return 1
}
md5sums=('1dbf4d57d99682ba466c7855ed555f19'
'6931ed658f5794e0654548c613381522')

ちなみに,xorg 1.8 での設定は Xorg 1.8 にアップデートしたときの設定の移行 – 落書き、時々落学 にあるように,/etc/X11/xorg.conf.d/に適当なファイルを作ればよい. 念の為,パッチの内容をここにも載せておく.

diff -Naur src.orig/evdev.c src/evdev.c
--- src.orig/evdev.c	2010-06-27 00:31:41.965189461 +0000
+++ src/evdev.c	2010-06-27 01:00:02.754603018 +0000
@@ -125,6 +125,32 @@
* cannot be used by evdev, leaving us with a space of 2 at the end. */
static EvdevPtr evdev_devices[MAXDEVICES] = {NULL};
+static void AddMadKey(EvdevPtr pEvdev, int trigger, int transfer, MadKeyType type)
+{
+    MadKeyList *keyList = pEvdev->madKeyList;
+    MadKeyList *key;
+    if (keyList != NULL) {
+        while (keyList->next != NULL)
+            keyList = keyList->next;
+    }
+    
+
+    key = xcalloc(sizeof(MadKeyList), 1);
+    if (key == NULL)
+        return;
+
+    key->madKey = &key->madKeyEntity;
+    key->madKey->trigger = trigger;
+    key->madKey->transfer = transfer;
+    key->madKey->type = type;
+    key->next = NULL;
+
+    if (keyList == NULL)
+        pEvdev->madKeyList = key;
+    else
+        keyList->next = key;
+}
+
static int EvdevSwitchMode(ClientPtr client, DeviceIntPtr device, int mode)
{
InputInfoPtr pInfo;
@@ -272,13 +298,26 @@
static int wheel_left_button = 6;
static int wheel_right_button = 7;
+static void
+EvdevEnqueKeyEvent(EvdevPtr pEvdev, int code, int value)
+{
+    EventQueuePtr pQueue = &pEvdev->queue[pEvdev->num_queue];
+    pQueue->type = EV_QUEUE_KEY;
+    pQueue->key = code + MIN_KEYCODE;
+    pQueue->val = value;
+    pEvdev->num_queue++;
+}
+
void
EvdevQueueKbdEvent(InputInfoPtr pInfo, struct input_event *ev, int value)
{
int code = ev->code + MIN_KEYCODE;
-    EventQueuePtr pQueue;
EvdevPtr pEvdev = pInfo->private;
+    int scanCode = ev->code;
+    unsigned int tmpScanCode = scanCode;
+    unsigned int lastScanCode = pEvdev->lastScanCode;
+
/* Filter all repeated events from device.
We'll do softrepeat in the server, but only since 1.6 */
if (value == 2
@@ -299,11 +338,87 @@
return;
}
-    pQueue = &pEvdev->queue[pEvdev->num_queue];
-    pQueue->type = EV_QUEUE_KEY;
-    pQueue->key = code;
-    pQueue->val = value;
-    pEvdev->num_queue++;
+   /*
+    * Sticky Key
+    */
+    if (value) {
+        if (pEvdev->stickyPhase == 1) {
+            pEvdev->stickyPhase = 2;
+            EvdevEnqueKeyEvent(pEvdev, pEvdev->stickyScanCode, TRUE);
+            goto madKeyFinish;
+        } else if (pEvdev->stickyPhase == 2) {
+            pEvdev->stickyPhase = 0;
+            EvdevEnqueKeyEvent(pEvdev, pEvdev->stickyScanCode, FALSE);
+            goto madKeyFinish;
+        }
+    } else {
+        MadKeyList *keyList;
+        for (keyList = pEvdev->madKeyList; keyList != NULL; keyList = keyList->next) {
+            MadKey *key = keyList->madKey;
+            if (key->type == MAD_KEY_STICKY
+                && key->trigger == scanCode
+                && lastScanCode == scanCode) {
+                if (pEvdev->stickyPhase == -1) { /* ignore */
+                    pEvdev->stickyPhase = 0;
+                    break;
+                } else {
+                    pEvdev->stickyPhase = 1;
+                    pEvdev->stickyScanCode = keyList->madKey->transfer;
+                    return;
+                }
+            }
+        }
+    }
+    
+    /*
+     * Pseudo Modifier
+     */
+    {
+        MadKeyList *keyList;
+        for (keyList = pEvdev->madKeyList; keyList != NULL; keyList = keyList->next) {
+            MadKey *key = keyList->madKey;
+            if (key->type == MAD_KEY_PSEUDO_MOD
+                && key->trigger == scanCode) {
+                if (lastScanCode == key->transfer) {
+                    tmpScanCode = lastScanCode;
+                    pEvdev->stickyPhase = -1;
+                } else if (value)
+                    scanCode = key->transfer;
+                else {
+                    if (lastScanCode == scanCode) {
+                        EvdevEnqueKeyEvent(pEvdev, key->transfer, FALSE);
+                        EvdevEnqueKeyEvent(pEvdev, key->trigger, TRUE);
+                    } else
+                        scanCode = key->transfer;
+                }
+                goto madKeyFinish;
+            }
+        }
+    }
+    
+    /*
+     * One Shot Modifier
+     */
+    if (!value) {
+        MadKeyList *keyList;
+        for (keyList = pEvdev->madKeyList; keyList != NULL; keyList = keyList->next) {
+            MadKey *key = keyList->madKey;
+            if (key->type == MAD_KEY_ONE_SHOT_MOD
+                && key->trigger == scanCode
+                && lastScanCode == scanCode) {
+                scanCode = key->transfer;
+                EvdevEnqueKeyEvent(pEvdev, lastScanCode, FALSE);
+                EvdevEnqueKeyEvent(pEvdev, scanCode, TRUE);
+                goto madKeyFinish;
+            }
+        }
+    }
+    
+  madKeyFinish:
+    if (value)
+        pEvdev->lastScanCode = tmpScanCode;
+    
+    EvdevEnqueKeyEvent(pEvdev, scanCode, value);
}
void
@@ -1112,6 +1227,8 @@
pInfo = device->public.devicePrivate;
pEvdev = pInfo->private;
+    pEvdev->lastScanCode = 0;
+    pEvdev->stickyPhase = 0;
/* sorry, no rules change allowed for you */
xf86ReplaceStrOption(pInfo->options, "xkb_rules", "evdev");
@@ -2103,6 +2220,62 @@
EvdevDragLockPreInit(pInfo);
}
+    if (pEvdev->flags & EVDEV_KEYBOARD_EVENTS)
+    {
+        pEvdev->madKeyList = NULL;
+        if (xf86FindOption(pInfo->options, "StickyShift")) {
+          AddMadKey(pEvdev, KEY_LEFTSHIFT, KEY_LEFTSHIFT, MAD_KEY_STICKY);
+          AddMadKey(pEvdev, KEY_RIGHTSHIFT, KEY_RIGHTSHIFT, MAD_KEY_STICKY);
+          xf86Msg(X_CONFIG, "%s: StickyShift enabled\n", pInfo->name);
+        }
+        if (xf86FindOption(pInfo->options, "StickyCtrl")) {
+          AddMadKey(pEvdev, KEY_LEFTCTRL, KEY_LEFTCTRL, MAD_KEY_STICKY);
+          AddMadKey(pEvdev, KEY_RIGHTCTRL, KEY_RIGHTCTRL, MAD_KEY_STICKY);
+          xf86Msg(X_CONFIG, "%s: StickyCtrl enabled\n", pInfo->name);
+        }
+        if (xf86FindOption(pInfo->options, "StickyAlt")) {
+          AddMadKey(pEvdev, KEY_LEFTALT, KEY_LEFTALT, MAD_KEY_STICKY);
+          AddMadKey(pEvdev, KEY_RIGHTALT, KEY_RIGHTALT, MAD_KEY_STICKY);
+          xf86Msg(X_CONFIG, "%s: StickyAlt enabled\n", pInfo->name);
+        }
+        if (xf86FindOption(pInfo->options, "PseudoModSpace")) {
+          int transfer = xf86SetIntOption(pInfo->options,
+                                          "PseudoModSpace",
+                                          MIN_KEYCODE) - MIN_KEYCODE;
+          AddMadKey(pEvdev, KEY_SPACE, transfer, MAD_KEY_PSEUDO_MOD);
+          xf86Msg(X_CONFIG, "%s: PseudoModSpace enabled\n", pInfo->name);
+        }
+        if (xf86FindOption(pInfo->options, "OneShotShift")) {
+          int transfer = xf86SetIntOption(pInfo->options,
+                                          "OneShotShift",
+                                          MIN_KEYCODE) - MIN_KEYCODE;
+          AddMadKey(pEvdev, KEY_LEFTSHIFT, transfer, MAD_KEY_ONE_SHOT_MOD);
+          AddMadKey(pEvdev, KEY_RIGHTSHIFT, transfer, MAD_KEY_ONE_SHOT_MOD);
+          xf86Msg(X_CONFIG, "%s: OneShotShift enabled\n", pInfo->name);
+        }
+        if (xf86FindOption(pInfo->options, "OneShotCtrl")) {
+          int transfer = xf86SetIntOption(pInfo->options,
+                                          "OneShotCtrl",
+                                          MIN_KEYCODE) - MIN_KEYCODE;
+          AddMadKey(pEvdev, KEY_LEFTCTRL, transfer, MAD_KEY_ONE_SHOT_MOD);
+          AddMadKey(pEvdev, KEY_RIGHTCTRL, transfer, MAD_KEY_ONE_SHOT_MOD);
+#ifdef XKB
+          if (xkb_options != NULL && strstr(xkb_options, "ctrl:swapcaps") != NULL)
+            AddMadKey(pEvdev, KEY_CAPSLOCK, transfer, MAD_KEY_ONE_SHOT_MOD);
+#endif
+          xf86Msg(X_CONFIG, "%s: OneShotCtrl enabled\n", pInfo->name);
+        }
+        if (xf86FindOption(pInfo->options, "OneShotAlt")) {
+          int transfer = xf86SetIntOption(pInfo->options,
+                                          "OneShotAlt",
+                                          MIN_KEYCODE) - MIN_KEYCODE;
+          AddMadKey(pEvdev, KEY_LEFTALT, transfer, MAD_KEY_ONE_SHOT_MOD);
+          AddMadKey(pEvdev, KEY_RIGHTALT, transfer, MAD_KEY_ONE_SHOT_MOD);
+          xf86Msg(X_CONFIG, "%s: OneShotAlt enabled\n", pInfo->name);
+        }
+
+				}
+
return pInfo;
}
diff -Naur src.orig/evdev.h src/evdev.h
--- src.orig/evdev.h	2010-06-27 00:31:41.965189461 +0000
+++ src/evdev.h	2010-06-27 01:02:37.194587398 +0000
@@ -103,6 +103,25 @@
int traveled_distance;
} WheelAxis, *WheelAxisPtr;
+/* key status data for mad-key system  */
+typedef enum {
+     MAD_KEY_STICKY,
+     MAD_KEY_PSEUDO_MOD,
+     MAD_KEY_ONE_SHOT_MOD,
+} MadKeyType;
+ 
+typedef struct {
+     int        trigger;
+     int        transfer;
+     MadKeyType type;
+} MadKey;
+ 
+typedef struct MadKeyList {
+     MadKey             madKeyEntity;
+     MadKey            *madKey;
+     struct MadKeyList *next;
+} MadKeyList;
+
/* Event queue used to defer keyboard/button events until EV_SYN time. */
typedef struct {
enum {
@@ -132,6 +151,17 @@
int delta[REL_CNT];
unsigned int abs, rel;
+    int                 lastScanCode;
+    int                 stickyScanCode;
+    /*
+     * -1: ignore
+     *  0: disabled
+     *  1: presss enabled
+     *  2: release enabled
+     */
+    int                 stickyPhase;
+    MadKeyList          *madKeyList;
+
/* XKB stuff has to be per-device rather than per-driver */
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 5
XkbComponentNamesRec    xkbnames;