top page > Klaviatur > here

キーの操作に伴うデータの流れ


概要

対象は次のようなものです.

  1. K/B は PC-98 専用 および AT 互換機 (PS/2 および USB)
  2. FreeBSD は 4 系以降
  3. console および X Window System (XFree86 4 系, X.Org 6 系)
    X.Org 7 系の HAL に関しては,X.Org on PC-98 (1), X.Org on AT (1) を参照して下さい.

目的は次のようなものです.

  1. キーボードに関する予備知識を提示する.
  2. キーの操作に伴うデータの流れを提示する.

目次

1. 予備知識
1.1 キーの分類
1.2 種々の scan code
2. console
3. X Window System
3.1 console との差異
3.2 データの構成とそれらの関係
3.3 補足
4. 参考資料

1. 予備知識

1.1  キーの分類

キーボード上のキーは,様々な視点によって分類されます.

例えば,機能で分類すれば次のようになります. ただし,一つのキーは修飾キーとの組み合わせにより複数の機能を持つのが普通です. (それも含めて機能の実現と割り当ては OS の役割であり,変更可能です.)

  • 修飾キー (Modifier)
    (1) [Shift], [Ctrl], [Alt] など,それらを押しながら別のキーを押すもの.
    (2) [Caps Lock], [Num Lock] のように Lock されるもの.
    (3) emacs における [ESC] のように,そのキーを押した効果が次の入力にのみ影響するもの.
      (これは "Latch" と呼ばれています.[1])
  • それ以外のキー
    (1) 文字もしくは文字列を入力するキー ([ESC], [Tab], [Enter] 等も含む.)
    (2) 編集キー (消去,カーソルの移動など.)
    (3) OS の種々の機能に結び付いているキー (boot や仮想 console の切り換えなど.)

また,xkb/geomerty, symbols では,機能よりもむしろ大雑把な配置で分類しています.

  • Alphanumeric : 英数キー
  • Function : ファンクション・キー
  • Editing : カーソルの移動等
  • Keypad : いわゆる 10 キーとその周辺
  • extra keys on 104-key "Windows95" keyboards
    (大雑把な配置で言えばこれらも Alphanumeric ですが,あえて区別されています.)

キーの挙動 (効き方) によっても分類できます. (AT 互換機ではこれらは OS が管理しており,変更可能です.)

  • 通常のキー.何度押しても同じ反応をする.
  • Lock Key. 押すごとにトグル式に on/off が切り換わる.
  • Radio Group. 例えば仮想 console を切り換える場合のように何かを on にすると,今まで on だったものが off になる.
  • その他,Overlay などがあります.

一方,キーを押した際に PC が認識する仕方,キーの送る信号という観点からの分類もできます.

  • PC に実際に信号 (scan code) を送るキー
    修飾キーも含めて,文字が入力されないキーも scan code は送っています.
  • K/B 内部で制御が閉じていて,PC には信号を送らないキー
    仕様どおりに作られたキーボードにはない,そのキーボード特有のキーです.
    例えば HH K/B や Libretto には [Fn] というキーがあります. (Function Key ではない!) 使い方は modifier に類似していますが,単独では scan code を送りません. このキーと組み合わせることにより,特定のキーの scan code が切り換わります. (PC からは,別のキーが押されているようにしか見えない.) より少ないキーで full key に近付けるためにこうしています.[2]
  • PC に信号は送られるが,それらが scan code の扱いを受けないキー
    具体的にどう実現しているかはわかりません. (そもそも信号を送る port が違うとか,port は同じだが何らかの方法で「本来の信号」とは別扱いにするとか.)
    例えば PC-9821 La13 や Libretto の特殊機能に関連するキー操作がこれです.

なお,PC-98 においては,次のような特有の「個性」があります.

  • 二つある [SHIFT][Enter] が各々独立でなく,同一の scan code を送るので区別できない.
    これらを区別する必要はないので,問題は生じないでしょう.
  • Windows Key [LWIN], [RWIN], [MENU] が正規の scan code を送っておらず,使用できない.
    どのような信号を送っているのかも不明. 普通はこれらを使わないので,問題ないでしょう.
  • Lock Key [CAPS], [かな] に対応する LED の on/off の制御は,キーボード側で行なわれている.
    [CAPS] だけは device driver により対処されています. [かな] は普通は使いません.

LED の点灯ですが,AT 互換機においてはその制御は PC の側から OS により行なわれています. 一方 PC-98 においては,[CAPS], [かな] の LED は両方ともキーボードの中で制御が完結しています. これらの点灯は,それらのキーに割り当てられた機能に依らず,キーそのものに連動しています. LED の状態の監視や点灯の制御を PC 側から行なうことも可能なようですが,FreeBSD ではそれをサポートしていません.

[1] Latch のもう一つの例に console におけるアクセント・キーがあります. (% jman 5 kbdmap を参照.) そのキーを押して放した後で文字のキー (あらかじめそのアクセントと関係付けておいたもののみ) を押すと,そのアクセント付きの文字が入力されます.
  X Window System でもこれに相当する "dead key" というものがあるようなのですが,使い方がわかりません. どのようなものがあるかは /usr/X11R6/lib/X11/locale/*/Compose をご覧下さい.

[2] この動作を software で実現するのが,挙動のところに記した "Overlay" です.

1.2  種々の scan code

先ず PC-98 専用,または AT 互換機の PS/2 キーボードを考えます. キーを押したり放したりすれば,例外のキーは別として,キーボードから PC に向けて信号が送られます. これが keyboard scan code です.[3]

PC 内部の keyboard controller は,それを変換して hardware で言えば CPU,software で言えば device driver に転送します. その変換された信号が system scan code です.

次に USB キーボードを考えます. これは keyboard scan code とは違う信号 (usbcode [4]) を送信しますが,device driver がそれを system scan code に変換します.[5]

よって,いずれの場合においても OS は system scan code の形で信号を受け取ります.

system scan code は,キーを識別するだけでなく,キーの状態の情報も含んでいます. すなわち,キーの状態を表わす最上位の 1 bit とキーを識別する 7 bit とから成ります.[6] 前者は,キーが押された場合に 0,放された場合に 1 です. キーが押された場合の system scan code を "make code",放された場合のそれを "break code" と呼びます. make code の値の範囲は 0-127,break code のそれは 128-255 です. (当然,同じキーについての二つの code の差は 128 です.)

[3] keyboard scan code に直にお目にかかることはありません. そういう機会は無い方が幸せです.
  Linux では入力を RAW モードに設定すればこれが得られるそうですが,試したことはありません.

[4] この信号の正式名称はわからないので,device driver で用いられている変数名で呼びます.

[5] source (/usr/src/sys/dev/usb/ukbd.c) の冒頭に

#define UKBD_EMULATE_ATSCANCODE 1

  という部分があります. これにより usbcode を system scan code へ変換することが選択されています.
  変換に必要なデータは driver がその source の一部として持っています.

[6] それゆえ,キーは最大 128 個しか識別できません.

2. console

以後,system scan code を device driver の中での変数名 scancode で呼ぶことにします. 前述のように,この中には二つの情報が含まれています. そこで,device driver の中では

keycode = scancode & 0x7F;

のように,新たな変数 keycode を導入してキーを識別する部分を分離しています.

次に,これはキーマップの表の管理法の都合なのですが,alock または ashift が ON の場合には,keycode の値に 128 を足して 128-255 の範囲の値に変換しています. そして,この keycode によってキーマップを参照しています. これを通じて,キーの操作とその結果 (文字の入力とか何らかの機能) とが結び付けられます. また,複数用意されているキーマップの選択や自身の手によるその変更を通じて,目的に応じた結果が得られるようになります.

キーマップの一例として /usr/share/syscons/keymaps/us.iso.kbd の抜粋を示します. 一番左の列は,実際には scancode でなく keycode です.

# $FreeBSD: src/share/syscons/keymaps/us.iso.kbd,v 1.16 2001/03/11 23:41:19 ache
 Exp $
#                                                         alt
# scan                       cntrl          alt    alt   cntrl lock
# code  base   shift  cntrl  shift  alt    shift  cntrl  shift state
# ------------------------------------------------------------------
  000   nop    nop    nop    nop    nop    nop    nop    nop     O
  001   esc    esc    esc    esc    esc    esc    debug  esc     O
  002   '1'    '!'    nop    nop    '1'    '!'    nop    nop     O
  003   '2'    '@'    nul    nul    '2'    '@'    nul    nul     O
(中略)
  016   'q'    'Q'    dc1    dc1    'q'    'Q'    dc1    dc1     C
(中略)
  103   fkey61 fkey61 fkey61 fkey61 fkey61 fkey61 boot   fkey61  O

(例 1) 英数キーの '1' は keycode = 002 です. 単独で押せば '1',shift + 1 で '!' が入力されます.

(例 2) 英数キーの 'q' は keycode = 016 です. 単独で押せば 'q',shift + q で 'Q' が入力されます.
  また,行末の "C" は,このキーが Caps Lock の影響を受けることを指します.

(例 3) [DEL] キーは keycode = 103 です. control + alt + DEL で reboot します.
  (ちなみに,Pey Pad の '.' と共用する方の [DEL] キーは keycode = 83 です.)
  "fkey61" の意味は,man keyboard に次のように書かれています.

     The function keys are numbered like this:
                   Delete                  key 61

  この "fkey61" は "del" とどう違うのか,なぜ使い分けているのかは分かりません.

3. X Window System

3.1  console との差異

console では,標準で用意されているキーマップの選択肢以外のことをしたければ,設定ファイルを直接書き換える以外に手段はありません. 一方 X Window System では,それ以外の手段が幾層にも備わっています. キーマップを確認したり変更するためのツールが用意されていますし,自作のプログラムの中からそれらの操作を行なうための関数も用意されています.

設定の仕方は,以下に示すように console よりもかなり複雑になっていますが,修飾キーの取り扱いなど,より合理的になっています.

また,console では文字コードの差異も含めて設定ファイルを指定します. 一方 X Window System では,XF86Config, xorg.conf に規定する us, ja のような「言語を表わすキーワード」に文字コードの区別はなく,それは環境変数 LANG により設定されます.

3.2  データの構成とそれらの関係

X Window System においても console と同様に scancode を出発点にしています. OS から受け取る scancode は,次のようにして最終的に得られる結果に変換されます.

  1. scancodekeycode に変換する.
  2. keycode 番号keycode 名と結び付ける.
  3. keycode 名keysym 名と結び付ける.
  4. keysym 名を ASCII 文字ストリングや機能と結び付ける.
  5. keycode と修飾キーから ASCII 文字ストリングを得る.

1. X Window System では,キーを物理的に識別するために keycode を用います. 押された,放されたという状態は "event" (KeyPress, KeyRelease) として別管理になりますが,ここでは触れません. (これは console の説明に現われた keycode と発想も名前も同じですが,別のものであり数値も違います.)

変数 keycode の値を "keycode 番号" と呼びます. その値域は 8-255 です. 単純に

keycode = (scancode & 0x7f) + 8

であれば,設定ファイルを追うのが楽です. 実際,PC-98 ではそうなっています. AT 互換機でも以前はそうでしたが,今はかなりのキーがそうなっていません.

以下では例として,AT 互換機における英数キー '1' と 'q' を考えます.
2. keycode 番号keycode 名とは /usr/X11R6/lib/X11/xkb/keycodes/xfree86 によって,次のように結び付けられています.

    <keycode 名> = keycode 番号;
    <AE01>       =  10;            ← キー '1'
    <AD01>       =  24;            ← キー 'q'

3. 一方,キーを論理的に識別するために keysym を用います. keycode 名keysym 名とは,例えば us101 K/B ならば /usr/X11R6/lib/X11/xkb/symbols/pc/us によって,次のように結び付けられています.

    key <keycode 名> { [keysym 名の列挙] };
    key <AE01>       { [ 1, exclam ] };
    key <AD01>       { [ q, Q      ] };

keysym 名/usr/X11R6/include/X11/keysymdef.h に定義されています.上の四つに対してこれを抜粋すると,次のようになっています.

#define XK_1                             0x0031  /* U+0031 DIGIT ONE */
#define XK_exclam                        0x0021  /* U+0021 EXCLAMATION MARK */

#define XK_q                             0x0071  /* U+0071 LATIN SMALL LETTER Q */
#define XK_Q                             0x0051  /* U+0051 LATIN CAPITAL LETTER Q */

厳密に区別するならば,"keysym" はここで定義された記号定数を,"keysym 値" はここで定義された数値を,"keysym 名" はこの記号定数から "XK_" を除いた文字列を指 します.

4. keysym 名 は,あくまでキーの持つ意味を記号化したものであり,そのキーが押された際に実際に得られる文字列を表わしているわけではありません. (ここでは 1 文字も「文字列」として扱います.) 実際に得られる文字列は「ASCII 文字ストリング」と呼ばれ,keysym と対応付けられています. 例えば keysym 名1, exclam, q, Q には各々 ASCII 文字ストリング "1", "!", "q", "Q" が対応しています.

これらの対応関係は,何らかの設定ファイルに書かれているのでなく,自動的に決められているようです.
(自作のプログラムからこれを変更するための関数も用意されています.)

5. /usr/X11R6/include/X11/keysymdef.h には,keysym として種々の修飾キーも定義されています.

/* Modifiers */
#define XK_Shift_L                       0xffe1  /* Left shift */
#define XK_Shift_R                       0xffe2  /* Right shift */

#define XK_Caps_Lock                     0xffe5  /* Caps lock */
#define XK_ISO_Lock                      0xfe01

/usr/X11R6/lib/X11/xkb/symbols/pc/pc では,次のように modifier 名 が定義されています.

    modifier_map  modifier 名  { keysym 名の列挙 };
    modifier_map  Shift        { Shift_L, Shift_R };
    modifier_map  Lock         { Caps_Lock, ISO_Lock };

さらに /usr/X11R6/lib/X11/xkb/types/basic では,次のように修飾キーが効く対象とその効き方が定義されています. このようにして,各々の修飾キーが各々のキーにどのように効くかが決まります.
(ここで type は各々のキーの属性であり,keysym から自動的に割り当てられるか,または /usr/X11R6/lib/X11/xkb/symbols/pc/pc にて規定します.)

    type "TWO_LEVEL" {                 ← この type には (ここの例の場合はキー '1')
        modifiers = Shift;             ← この modifier 名の修飾キーが効く.
        map[Shift] = Level2;           ← Shift によって Level2 が選択される.
        level_name[Level1] = "Base";
        level_name[Level2] = "Shift";
    };

    type "ALPHABETIC" {                ← この type には (ここの例の場合はキー 'q')
        modifiers = Shift+Lock;        ← この二つの modifier 名の修飾キーが効く.
        map[Shift] = Level2;           ← Shift によって Level2 が選択される.
        map[Lock] = Level2;            ← Lock  によって Level2 が選択される.
        level_name[Level1] = "Base";
        level_name[Level2] = "Caps";
    };

/usr/X11R6/lib/X11/xkb/symbols/pc/us に戻れば,Level の後に続く数字は列挙された順番を指します.

    key <AE01> { [ 1, exclam ] };      ← Shift すれば後者が選ばれる.
    key <AD01> { [ q, Q      ] };      ← Shift または Lock すれば後者が選ばれる.

関数 XLookupString() は,keycode と修飾キーの状態とから,また必要な場合には環境変数 LANG も考慮し,それらに対応する keysym と ASCII 文字ストリングとを返してくれます.

3.3  補足

1, 「X Window System の Server (/usr/X11R6/bin/XFree86, Xorg) が起動すると,キーボードは RAW モードになる」(すなわち「Server は keyboard scan code を受け取る」) と書いてある資料もありましたが,少なくとも XFree86, X.Org と PC-98, AT 互換機との組み合わせでは,受け取るのは system scan code の方です.

X Server を ports を利用して導入する場合,それらは /usr/ports/x11-servers/XFree86-4-Server または xorg-server にあります. そこで make をすると,work という directory が作成され,その中に source が展開されます. キーボードに関連するのはそこから辿って xc/programs/Xserver/hw/xfree86/common/ にある xf86Events.catKeynames.h などです. キーコードの変遷に注目して抜粋すると,次のようになります.

xf86PostKbdEvent(unsigned key)       ← 変数 key は system scan code です.

  int scanCode = (key & 0x7f);       ← 変数 scanCode は,キーを識別する部分です.

  ここで,特定のいくつかのキーの scanCode の値を変更します.

  keycode = scanCode + MIN_KEYCODE;  ← keycode は scanCode に 8 を加えて得られます.

2, 設定に関連して次々と用語が登場するので,それらをなるべく変数名や設定ファイルの中で使われているキーワードを使って表わすようにしました. 次の表に用語の比較を示します.

この Home Page XKB 関連の文書 日本語の書籍,雑誌記事
scancode scan code, raw keycodes reported by the device スキャン・コード
keycode key code, keycode キー・コード
keycode 番号 keycode number キー・コード番号
keycode 名 keycode symbolic name
keysym キー・シンボル
keysym 値 keysym value keysym 値
keysym 名 symbols symbolic name, keysym name keysym 名
修飾キー modifier 修飾キー
modifier 名 modifier name 修飾キー名,モデファイア名
ASCII 文字ストリング actual symbol ASCII 文字ストリング

3, /usr/X11R6/lib/X11/xkb/types/basic では,type "ALPHABETIC" のために二つの修飾キーを同時に使った状態は定義されていません. 本来ならば,次のような定義を追加する必要があるでしょう. なぜこれなしに正常に動作しているのかは解りません. (どこかでちゃんと対処がなされているのですから,これをあえて加えない方が無難でしょう.)

       map[Shift+Lock] = Level1;

4, /usr/X11R6/include/X11/keysymdef.h には,次のような文字や機能も定義されています.

#define XK_KP_0                          0xffb0  ← Key Pad の '0'
#define XK_F1                            0xffbe  ← Function Key の [F1]
#define XK_kana_A                        0x04b1  ← 半角カナの「ア」
#define XK_Cyrillic_YA                   0x06f1  ← 1 byte キリル文字の "Я"

半角カナの「ア」の文字コードは 0xb1,KOI8-R の キリル文字の "Я" の文字コードは 0xf1 です. (環境変数 LANG として ISO8859-5 を設定した場合には,"Я" はその文字コードである 0xcf に変換されます.) このように,英字のアルファベットに相当する文字は,その文字コードに言語を識別するための 1 byte を付加して定義されています.

また,次のような文字や機能も定義されています.

#define XK_BackSpace                     0xff08  /* Back space, back char */
#define XK_Tab                           0xff09
#define XK_Return                        0xff0d  /* Return, enter */
#define XK_Delete                        0xffff  /* Delete, rubout */

#define XK_Left                          0xff51  /* Move left, left arrow */
#define XK_Up                            0xff52  /* Move up, up arrow */
#define XK_Right                         0xff53  /* Move right, right arrow */
#define XK_Down                          0xff54  /* Move down, down arrow */

TabReturn は,それらの ASCII code に 0xff が付加された値です.

5. 自分で設定ファイルを追うことなしに現在の設定を確認したいならば,xev, xkbprint, xkbcomp -xkb, xmodmap -pk を活用します. 詳しくは 各種のツール を参照して下さい.

注意しなければならないのは,これらのツールの中でキーボードから実際に信号を受け取るのは xev だけだということです. 他のツールは,例え keycode 番号が違っていたとしてもそれを認識できず,設定ファイルに一貫性さえあればエラーになりません.

4. 参考資料

PC-98 用のキーボードに関しては,NEC や Borland C++ のマニュアルに記載されたキーボード BIOS に関する記述を参考にしました. 申し訳ありませんが,当方の手元にあるのは部分的なコピーなので,資料名はわかりません. これらに Windows Key に関する記載はありません. MS-DOS のキーボード BIOS ではこれらのキーは検知されないことは,実際にプログラムを作って確認しています.

1.2 節の AT 互換機用のキーボードの scan code に関しては,次の資料を参考にしました. ただし,OS は Linux や FreeDOS です. この記事に示された system scan code の値が一部のキーに対して keycodes の表と違うので要注意です. (FreeBSD の driver が keyboard controller から得られた値をいじっているのか?)

[1] 西田 亙「GCC プログラミング工房」 UNIX USER (ソフトバンク)
  第 12 回「踊るキーボード LED」(2002.11)
  第 13 回「FreeDOS でキーボードを探る」(2002.12)
  第 14 回「指先からの旅」(2003.1)
  第 15 回「キー入力処理を極める!」(2003.2)

3.2 節に関しては,次の書籍を参考にしました.

[2] 木下 凌一,林 秀幸「X-Window Ver.11 プログラミング (第 2 版)」 日刊工業新聞社 (1993)

[3] 砂原 秀樹 ら「プロフェッショナル BSD」pp.230-232, アスキー出版局 (1994)

設定ファイルの書式等,さらに詳細な情報は console におけるキーボードX Window System におけるキーボード を参照して下さい.