Linux で SandS の実現を目指す : xf86-input-evdev の修正(メモ)

追記(2011年10月8日)

定家さんのコードの2.6.2がリリースされました。 https://gitorious.org/at-home-modifier/pages/Home 詳細は以下のURLで。 http://gitorious.org/at-home-modifier/at-home-modifier/blobs/raw/master/README

追記(2011年2月21日)

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

SandSって?

SandS とは、入力システムの機能で Spaceを押すとShiftで離すとSpaceというもの。 SKKとの相性が抜群です。

現状

少なくとも2つの方法が存在している。 (1) キーボードカスタマイズの魅力 — ありえるえりあ キーボードドライバ xf86-input-keyboard に修正を加える。 (2) no title XGrabKeyboard でキー入力を横取りし XTest でフェイクキーを送る (1) は xf86-input-keyboard ではなく、 xf86-input-evdev を使用しているのでそのままでは使用不可。 (2) は正常動作しない。auto repeat を切ってもなぜか、押しっぱなしにしていると、 2回イベントが送られてくる。

環境

  • OS ArchLinux
  • kernel 2.6.32.9
  • xorg-server 1.7.5
  • xf86-input-evdev 2.3.2

作戦会議

汎用性が高いのは(2)の方法か。しかし、XGrabKeyboardで横取りしちゃうって なんかこわい。しかも、うまくいかない理由がよく分からない。 となると、残るは、(1)の方法を evdev に対応させる、となる。

evdev って何よ?

Xでホットプラグ(お手軽接続:再起動等無しで、外部装置を接続、認識する)を行うためのドライバー。 マウスとキーボードを担当する。 ちなみに、ホッツプラグは hal と dbus を利用しているんだとか。 でも、hal って廃止予定だったような。 より、詳しくは以下の ArchLinux の Wiki で Xorg – ArchWiki

で、できそうなの?

(1)をもうすこし詳しく見てみる。 kbd.c と xf86OSkbd.h の両方を変更している。

kbd.cでの変更箇所

  • 関数 AddMadKey の追加
  • 関数 KbdPreInt の変更
    • xf86FindOptionを使い、AddMadKeyを行う
  • 関数 InitKBD の変更
    • lastScanCode, stickyPhase の初期化
  • 関数 PostKbdEvent の変更
    • tmpScanCode, lastScanCode のローカル変数宣言
    • Sticky, Psedo, One-Shot 等の機能追加

xf86OSkbd.hの変更

  • 構造体 MadKeyの追加
  • 構造体 KbdDevRec, *KbdDevPtr に lastScanCode, stickyScanCode, stickyPahse, madKeyListの追加

結論

できるよ、多分。

つぎに対応箇所を evdev から探す。

EvdevRec, *EvdevPtr がドライバかと、こいつに内部変数を持たせればよさそうな雰囲気である。 イベント処理関数の関係(大雑把)。

EvdevProcessEvent
|
+–EvdevProccessKeyEvent
|  |
|  +–EvdevProcessButtonEvent
|     |
|     +–EvdevQueueKbdEvent–[Code] pQueue = &pEvdev->queue[pEvdev->num_queue]
|
+–EvdevProcessSyncEvent
|
+–EvdevPostQueuedEvents
|
+– EV_QUEUE_KEY -> xf86PostKeyboardEvent

これを見ると、xf86PostKeyboardEventを呼び出している、EvdevQueueKbdEventに Sticky, Psedo, One-Shot 等の機能追加部分を足せばよさそうである。 初期化関数の関係(大雑把)

EvdevInit
|
+–EvdevAddKeyClass
|
+–SetXkbOption– xf86SetStrOption
EvdevPreInit
|
+– EvdevSetCalibration
+– EvdevAddDevice
+– EvdevMBEmuPreInit
+– EvdevWheelEmuPreInit
+– EvdevDragLockPreInit

EvdevAddKeyClassでlastScanCode, stickyPhase の初期化する感じか。 ところで、EvdevKeyPreInitがない件について。 多分、オプション設定の読み込みとかは EvdevPreInit でやるはず。

if (pEvdev->flags & EVDEV_KEYBOARD_EVENTS)

とかすればよいか。

で、パッチができた。

http://dl.dropbox.com/u/662567/xf86-input-evdev-2.3.2-mad-key.patch 結果、ほとんど、(1)と同じ。 あとは、これを適用してコンパイルすればよい。 ArchLinux では ABSを利用するのが楽。以下 PKGBUILD。

pkgname=xf86-input-evdev
pkgver=2.3.2
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.7.0’ ‘inputproto>=2.0’ ‘randrproto>=1.3.1’)
conflicts=(‘xorg-server<1.7.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=(‘b2bfe368022eedf2671ee28daba31efc’ ‘b414ce6ea7c63195730c3b7767609217’)

オプションを有効にする。

/etc/X11/xorg.conf に何か書いても有効にはなりません。 evdev をつかっているので、オプション等は hal 経由になる。 といことで、/etc/hal/fdi/policy/10-keymap.fdi に書く。

<merge key=“input.x11_options. PseudoModSpace” type=“string”>50</merge>

これは、/etc/X11/xorg.conf に以下を書いたのと同様。

Option “PseudoModSpace” “50”

現在の 10-keymap.fdi の内容。

<?xml version=“1.0” encoding=“ISO-8859-1”?> <!– -- SGML -- –>
<deviceinfo version=“0.2”>
  <device>

&lt;match key="info.capabilities" contains="input.keymap"&gt;
  &lt;append key="info.callouts.add" type="strlist"&gt;hal-setup-keymap&lt;/append&gt;
&lt;/match&gt;

&lt;match key="info.capabilities" contains="input.keys"&gt;
  &lt;merge key="input.x10_driver" type="string"&gt;evdev&lt;/merge&gt;
  &lt;merge key="input.x11_options.XkbRules" type="string"&gt;xorg&lt;/merge&gt;
  &lt;merge key="input.x11_options.XkbModel" type="string"&gt;jp106&lt;/merge&gt;
  &lt;merge key="input.x11_options.XkbLayout" type="string"&gt;jp&lt;/merge&gt;
  &lt;merge key="input.x11_options.XkbVariant" type="string"&gt;&lt;/merge&gt;
  &lt;merge key="input.x11_options.XkbOptions" type="string"&gt;terminate:ctrl_alt_bksp&lt;/merge&gt;
  &lt;merge key="input.x11_options.PseudoModSpace" type="string"&gt;50&lt;/merge&gt;
&lt;/match&gt; 

</device> </deviceinfo>

参考

参考にしたもの。 kernel26/src/linux-2.6.32/Documentation/input.txt キーボードカスタマイズの魅力 — ありえるえりあ no title 404 Not Found XorgInputHOWTO

patch

一応、パッチを全部のせておく。

diff -crN src.orig/evdev.c src/evdev.c
*** src.orig/evdev.c    2010-03-06 06:58:52.897183337 +0900
— src/evdev.c 2010-03-06 10:29:38.118835311 +0900


*** 137,142 **** — 137,170 —- * 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 size_t CountBits(unsigned long *array, size_t nlongs) { unsigned int i;


*** 252,265 **** static int wheel_left_button = 6; static int wheel_right_button = 7; void EvdevQueueKbdEvent(InputInfoPtr pInfo, struct input_event ev, int value) { int code = ev->code + MIN_KEYCODE; static char warned[KEY_CNT]; - EventQueuePtr pQueue; EvdevPtr pEvdev = pInfo->private; / Filter all repeated events from device. We’ll do softrepeat in the server, but only since 1.6 */ if (value == 2 — 280,307 —- 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; static char warned[KEY_CNT]; 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


*** 293,303 **** return; } ! pQueue = &pEvdev->queue[pEvdev->num_queue]; ! pQueue->type = EV_QUEUE_KEY; ! pQueue->key = code; ! pQueue->val = value; ! pEvdev->num_queue++; } void — 335,421 —- return; } ! /* ! * 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


*** 1160,1165 **** — 1278,1286 —- 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”); SetXkbOption(pInfo, “xkb_rules”, &pEvdev->rmlvo.rules);


*** 2142,2147 **** — 2263,2326 —- 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 -crN src.orig/evdev.h src/evdev.h *** src.orig/evdev.h 2010-03-06 06:58:52.897183337 +0900 — src/evdev.h 2010-03-06 07:23:23.286619582 +0900


*** 89,94 **** — 89,114 —- 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 {


*** 117,122 **** — 137,154 —- 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