キーマッピング
Xlib 内部には、キーコード (物理キー番号) に対応する KeySym のリストと、X のキー修飾子 (Shift, Lock, Control, Mod1〜Mod5) に対応するキーコードのリストがあります。
X サーバーは、これらのデータを接続開始時やマッピング変更時にクライアントに送って、Xlib に保存させます。
Xlib は、このマッピングデータを使って、キーが押された時にキーコードから KeySym に変換したり、修飾キーが押された時に、対応する修飾子を ON にします。
X サーバーは、これらのデータを接続開始時やマッピング変更時にクライアントに送って、Xlib に保存させます。
Xlib は、このマッピングデータを使って、キーが押された時にキーコードから KeySym に変換したり、修飾キーが押された時に、対応する修飾子を ON にします。
プログラム
まずは、現在のキーマッピングを表示してみます。
<15-keymap.c>
※Xlib には、キーコードから KeySym を取得する XKeycodeToKeysym 関数が存在しますが、現在非推奨となっているため、キーマッピングのデータから KeySym を取得しています。
関数を使って取得したい場合は、代わりに XKB 拡張機能を使ってください。
<15-keymap.c>
#include <stdio.h> #include <X11/Xlib.h> #include <X11/keysym.h> int main(int argc,char **argv) { Display *disp; KeySym *buf,*ptr,ksym; XModifierKeymap *mmap; KeyCode *kptr; char *name; int i,j,cnt,min,max,num; disp = XOpenDisplay(NULL); if(!disp) return 1; XDisplayKeycodes(disp, &min, &max); printf("min: %d, max: %d\n", min, max); //キーマップ printf("\n--- key map ---\n"); cnt = max - min + 1; buf = ptr = XGetKeyboardMapping(disp, min, cnt, &num); for(i = 0; i < cnt; i++) { printf("[%d]", min + i); for(j = 0; j < num; j++, ptr++) { if(*ptr == NoSymbol) printf(" -"); else if(*ptr == XK_VoidSymbol) printf(" <void>"); else { name = XKeysymToString(*ptr); printf(" %s", (name)? name: "<?>"); } } printf("\n"); } //修飾子マップ printf("\n--- modifier map ---\n"); mmap = XGetModifierMapping(disp); kptr = mmap->modifiermap; for(i = 0; i < 8; i++) { printf("[%d]", i); for(j = 0; j < mmap->max_keypermod; j++, kptr++) { printf(" %d", *kptr); if(*kptr) { ptr = buf + (*kptr - min) * num; ksym = (ptr[0] == NoSymbol)? ptr[1]: ptr[0]; if(ksym != NoSymbol) { name = XKeysymToString(ksym); if(name) printf("(%s)", name); } } } printf("\n"); } // XFree(buf); XFreeModifiermap(mmap); XCloseDisplay(disp); return 0; }
※Xlib には、キーコードから KeySym を取得する XKeycodeToKeysym 関数が存在しますが、現在非推奨となっているため、キーマッピングのデータから KeySym を取得しています。
関数を使って取得したい場合は、代わりに XKB 拡張機能を使ってください。
実行結果
min: 8, max: 255 --- key map --- [8] - - - - - - - [9] Escape - Escape - - - - [10] 1 exclam 1 exclam - - - [11] 2 quotedbl 2 quotedbl - - - ... --- modifier map --- [0] 50(Shift_L) 62(Shift_R) 0 0 [1] 66(Eisu_toggle) 0 0 0 [2] 37(Control_L) 105(Control_R) 0 0 [3] 64(Alt_L) 108(Alt_R) 204(Alt_L) 205(Meta_L) [4] 77(Num_Lock) 0 0 0 [5] 203(ISO_Level5_Shift) 0 0 0 [6] 133(Super_L) 134(Super_R) 206(Super_L) 207(Hyper_L) [7] 92(ISO_Level3_Shift) 0 0 0
関数
キーコードの範囲を取得
void XDisplayKeycodes(Display *display, int *min_keycodes_return, int *max_keycodes_return);
指定 Display でサポートされているキーコードの最小値と最大値を取得します。
最小値は8以上で、最大値は 255 以下です。
キーマッピングを取得
KeySym *XGetKeyboardMapping(Display *display, KeyCode first_keycode, int keycode_count, int *keysyms_per_keycode_return);
first_keycode から keycode_count 個のキーコードに対応する、KeySym の配列を取得します。
keysyms_per_keycode_return に、1つのキーコードごとの KeySym の個数が入ります。
確保された KeySym の配列が返ります。
first_keycode のキーコードから、(keyms_per_keycode_return x keycode_count) 個分のデータとなります。
使用後は XFree() で解放します。
KeySym の名前を取得
char *XKeysymToString(KeySym keysym);
KeySym の名前 (マクロの "XK_" を除外した名前) を返します。
返された文字列は静的領域にあるため、解放などはしないでください。
指定された KeySym が定義されていない場合、NULL を返します。
修飾子のマッピング
XModifierKeymap *XGetModifierMapping(Display *display); XFreeModifiermap(XModifierKeymap *modmap); typedef struct { int max_keypermod; /* 1つの修飾子の KeyCode 数 */ KeyCode *modifiermap; /* max_keypermod x 修飾子8個 */ } XModifierKeymap;
XGetModifierMapping() で、修飾子のマッピングデータを取得します。
XFreeModifiermap() で、返された構造体を解放します。
キーマッピング
まず、XDisplayKeycodes() で、キーコードの最小値・最大値を取得します。
基本的に、8 と 255 です。
その後、XGetKeyboardMapping() でキーマッピングを取得します。
返された配列には、1つのキーコードに対して、複数 (固定数) の KeySym のリストがあります。
修飾子の状態によって、何番目の KeySym が使用されるかが決まります。
基本的に、8 と 255 です。
その後、XGetKeyboardMapping() でキーマッピングを取得します。
返された配列には、1つのキーコードに対して、複数 (固定数) の KeySym のリストがあります。
修飾子の状態によって、何番目の KeySym が使用されるかが決まります。
KeySym リスト
NoSymbol (0) は、未使用・未定義であることを表します。
リストの全てが NoSymbol であれば、使用されないキーです。
上記の結果例では、1つのキーコードに対して7個のリストがありますが、キーイベントによってキーコードから KeySym を取得する場合は、最初の4つのみを使います。
グループは、XK_Mode_switch が指定されたキーによって切り替えられます。
Shift などのキーとは別に、これを修飾子として割り当てると、修飾子が ON ならグループ2、OFF ならグループ1の KeySym が使われます。
グループによる区別がなければ、グループ1とグループ2は同じ値になります。
基本的に、グループ内の1番目は、通常時のキーで、グループ内の2番目は、+Shift 時のキーや、テンキーのキーです。
アルファベットの場合、1番目は小文字で、2番目は大文字です。
リストの全てが NoSymbol であれば、使用されないキーです。
上記の結果例では、1つのキーコードに対して7個のリストがありますが、キーイベントによってキーコードから KeySym を取得する場合は、最初の4つのみを使います。
グループ
KeySym リストの1・2番目は「グループ1」時のキーで、3・4番目は「グループ2」時のキーを示します。グループは、XK_Mode_switch が指定されたキーによって切り替えられます。
Shift などのキーとは別に、これを修飾子として割り当てると、修飾子が ON ならグループ2、OFF ならグループ1の KeySym が使われます。
グループによる区別がなければ、グループ1とグループ2は同じ値になります。
基本的に、グループ内の1番目は、通常時のキーで、グループ内の2番目は、+Shift 時のキーや、テンキーのキーです。
アルファベットの場合、1番目は小文字で、2番目は大文字です。
CapsLock、ShiftLock、NumLock
- キーマッピング内で XK_Caps_Lock が指定されており、修飾子のマッピングで、Lock 修飾子にそのキーコードが割り当てられている場合、Lock 修飾子 (LockMask) は CapsLock として解釈されます。
# キー [66] Eisu_toggle Caps_Lock Eisu_toggle Caps_Lock - - - # 修飾子 [1] 66(Eisu_toggle) 0 0 0
この場合、keycode 66 のグループ1・2の1番目が英数キーで、2番目が XK_Caps_Lock です。
+Shift でなければ XK_Eisu_toggle で、+Shift の場合は XK_Caps_Lock になります。
修飾子の [1] (Lock) にキーコード 66 が割り当てられており、そこに XK_Caps_Lock があるので、このキーは CapsLock として扱われます。
- XK_Shift_Lock がキーマッピング内にあり、Lock 修飾子にそのキーコードが割り当てられている場合、Lock 修飾子は ShiftLock として解釈されます。
# キー [50] Shift_L - Shift_L - - - - [62] Shift_R - Shift_R - - - - # 修飾子 [0] 50(Shift_L) 62(Shift_R) 0 0
この場合、XK_Shift_Lock が定義されていないので、ShiftLock としては扱われません。
- Lock 修飾子が、CapsLock と ShiftLock の両方として解釈できる場合は、CapsLock として解釈されます。
- XK_Num_Lock がキーマッピング内にあり、Mod1〜Mod5 のいずれかの修飾子に割り当てられている場合、その修飾子は numlock 修飾子と呼ばれ、テンキーは XK_KP_* の KeySym となります。
# キー [77] Num_Lock - Num_Lock - - - - # 修飾子 [4] 77(Num_Lock) 0 0 0
この場合、XK_Num_Lock は Mod2 (Mod1〜Mod5 は 3〜7 番目) に割り当てられています。
キーコードから KeySym への変換
修飾子の状態により、以下の順で KeySym が取得されます。
- NumLock が ON で、2番目の KeySym が XK_KP_* の場合 (テンキー)。
Shift が ON、または、ShiftLock として解釈される Lock が ON の場合は、1番目の KeySym が使用され、それ以外は2番目の KeySym が使用されます。 - Shift と Lock が両方とも OFF の場合、1番目の KeySym が使用されます。
- CapsLock として解釈される Lock が ON で、Shift が OFF の場合 (アルファベットは大文字、記号は通常)。
1番目の KeySym が使用されますが、その KeySym が小文字のアルファベットの場合は、大文字の KeySym が使用されます。 - CapsLock として解釈される Lock が ON で、Shift が ON の場合 (アルファベットは小文字、記号は +Shift)。
2番目の KeySym が使用されますが、その KeySym が大文字のアルファベットの場合は、小文字の KeySym が使用されます。 - Shift が ON、または ShiftLock として解釈される Lock が ON か、その両方の場合、2番目の KeySym が使用されます。
修飾子マッピング
X の Shift, Lock, Ctrl, Mod1〜Mod5 の各修飾子には、それぞれ複数のキーを割り当てることができます。
Shift や Ctrl には、左右に2つのキーがあるため、2つのキーを割り当てることになります。
X では、各修飾子に、最大8個のキーを割り当てることができます。
XGetModifierMapping() で、修飾子のマッピングを取得することができます。
XModifierKeymap 構造体の modifiermap は、キーコードの配列になっており、[0] Shift [1] Lock [2] Ctrl [3-7] Mod1〜Mod5 の順で、全8個の修飾子に対応する、各 max_keypermod 個分のキーコードが含まれています。
値が 0 の場合は、キーコードなしとなります。
Shift や Ctrl には、左右に2つのキーがあるため、2つのキーを割り当てることになります。
X では、各修飾子に、最大8個のキーを割り当てることができます。
XGetModifierMapping() で、修飾子のマッピングを取得することができます。
XModifierKeymap 構造体の modifiermap は、キーコードの配列になっており、[0] Shift [1] Lock [2] Ctrl [3-7] Mod1〜Mod5 の順で、全8個の修飾子に対応する、各 max_keypermod 個分のキーコードが含まれています。
値が 0 の場合は、キーコードなしとなります。
[0] 50(Shift_L) 62(Shift_R) 0 0 [1] 66(Eisu_toggle) 0 0 0 [2] 37(Control_L) 105(Control_R) 0 0 [3] 64(Alt_L) 108(Alt_R) 204(Alt_L) 205(Meta_L) [4] 77(Num_Lock) 0 0 0 [5] 203(ISO_Level5_Shift) 0 0 0 [6] 133(Super_L) 134(Super_R) 206(Super_L) 207(Hyper_L) [7] 92(ISO_Level3_Shift) 0 0 0
キーマッピングの変更時
キーマッピングが変更された場合、Xlib 内部のデータを更新する必要があるため、X サーバーはすべてのクライアントに MappingNotify イベントを報告します。
(このイベントは常に送信されます)
XMappingEvent 構造体の request が、MappingKeyboard または MappingModifier の場合は、XRefreshKeyboardMapping(XMappingEvent *) を呼び出してキーマッピングを更新します。
(このイベントは常に送信されます)
XMappingEvent 構造体の request が、MappingKeyboard または MappingModifier の場合は、XRefreshKeyboardMapping(XMappingEvent *) を呼び出してキーマッピングを更新します。
typedef struct { int type; unsigned long serial; Bool send_event; Display *display; Window window; int request; int first_keycode; int count; } XMappingEvent;
case MappingNotify: if(ev.xmapping.request == MappingKeyboard || ev.xmapping.request == MappingModifier) { XRefreshKeyboardMapping((XMappingEvent *)&ev); } break;