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>
<match key="info.capabilities" contains="input.keymap">
<append key="info.callouts.add" type="strlist">hal-setup-keymap</append>
</match>
<match key="info.capabilities" contains="input.keys">
<merge key="input.x10_driver" type="string">evdev</merge>
<merge key="input.x11_options.XkbRules" type="string">xorg</merge>
<merge key="input.x11_options.XkbModel" type="string">jp106</merge>
<merge key="input.x11_options.XkbLayout" type="string">jp</merge>
<merge key="input.x11_options.XkbVariant" type="string"></merge>
<merge key="input.x11_options.XkbOptions" type="string">terminate:ctrl_alt_bksp</merge>
<merge key="input.x11_options.PseudoModSpace" type="string">50</merge>
</match>
</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
作成者 Toru Mano
最終更新時刻 2023-01-01 (c70d5a1)