親指シフト及びNICOLAの課題
 親指シフトおよびその後継のNICOLA規格は、「親指キー」と「文字キー」とを順不同で同時性を意図して打鍵する「同時打鍵」により、文字キーの単独押しとは異なる文字(例えば、濁点付きカナ文字)を出力します。これによりユーザは、仮名文字の配列を覚えやすくなり、かつ入力しやすくなります。また、ホームポジションとその1段下および1段上に全ての仮名文字が配置されているので、ユーザは、ホームポジションからあまり指を離さずに仮名文字を入力可能となります。
 その代わり、文字キーを打鍵しても親指キーの状態が確定するまで待たねばならないため、文字キーが遅れて表示されるという課題があります。この遅れは、同時打鍵の判定時間と等しく、50mSEC~200mSECです。
このタイミングチャートは、従来のNICOLA規格のものです。文字キー$M$と親指キー$O$のグラフは、Hレベルが押されていない状態を示し、Lレベルが押されている状態を示します。タイマは、設定値($T_{th}$)をカウントダウンして、0になったときにタイムアウト動作を行うカウントダウンタイマです。時間($T_{th}$)は、同時打鍵の判定時間です。最下段の「状態」は、1が初期状態を示し、2が文字キー押下状態を示します。
文字キー$M$が押下されてから、キーボードまたはエミュレータがこの文字キー$M$をアプリケーションに出力するまで、時間$T_{th}$だけ遅延します。
文字キーの表示遅延の解決手段
 文字キーの表示遅延に対する解決手段が、零遅延モードです。零遅延モードは、聖人さんが開発された親指の友 Mk-2 キーボードドライバにて初搭載されました。
 聖人さんが零遅延モードについて書かれた記事は、以下です。
 親指シフトの遅延を体感上無くす実験(親指の友 Mk-2 V2.0L20~)とWindows10動作確認
 ソフト屋のための「英字入力時の親指シフト」(キー入力における小指の負担軽減策とか)
これら記事を参考にして、キーボード配列エミュレーションソフトウエア「紅皿」では、零遅延モードを実装いたしました。紅皿は、文字キー$M$が押下されたときにすぐさま現在の状態を反映したコードを出力します。所定時間が経過して親指キー$O$のシフト状態が確定したとき、先に出力したコード以外を出力すべきならば、バックスペースキーに続いて確定したコードを出力します。
具体的にいうと、紅皿は、文字キー$M$が押下されたとき、すぐさま単独打鍵$M$をアプリケーションに出力して、$T_{th}$をタイマに設定します。そして、タイマがタイムアウトして、同時打鍵の判定時間$T_{th}$が経過したとき、単独打鍵$M$の出力を確定します。なお、タイマの破線は、停止していることを示します。
紅皿のモード遷移表
 以下、キーボード配列エミュレーションソフトウエア「紅皿」ver.0.1.3.4 ver.0.1.3.5 のモード遷移表に基づき、紅皿に搭載された零遅延モードについて説明します。
 このモード遷移表において、文字キー$M$とは、キーボード最上段の"1"から""と、2段目の"q"から"["と、3段目の"a"から"]"と、4段目の"z"から""までをいいます。
 親指キー$O$とは、紅皿のデフォルトでは"無変換"と"変換"です。なお、左親指キー$L$および右親指キー$R$として記載している場合もあります。
 紅皿において初期化とは、修飾キー$X$を押下することをいいます。修飾キーは、以下のうち何れかです。
Enter,Space,Tab,Backspace,Delete,Insert,Home, End, PgUp, PgDn,全角/半角,ひらがな/カタカナキー,英数,F1~F24,Esc,AppsKey,PrintScreen,Pause,Break,Sleep,Help,LCtrl,RCtrl,LAlt,RAlt,LShift,RShift,LWin, RWin
 ver.0.1.3.5において、このモード遷移表のS5)の文字キーオフを、MO出力に変更しました。これにより高速打鍵時にスムーズにシフト文字を出力可能です。
| S1) 初期状態 | S2) Mオン状態 | S3) Oオン状態 | S4) M→Oオン状態 | S5) O→Mオン状態 | S6) O→M→Oオフ状態 | |
|---|---|---|---|---|---|---|
| 初期化 | - | M出力、 S1)へ | O出力、 S1)へ | MO出力、 S1)へ | MO出力、 S1)へ | セットされていた MO出力、 S1)へ | 
| 文字キー(M)オン | Mセット、 S2)へ | セットされていたM出力、 新Mセット、 S2)まま | Mセット、 S5)へ | 処理A (3キー判定) | MO出力、 新Mセット、 S2)へ | セットされていた MO出力、 新Mセット、 S2)へ | 
| 親指(O)オン | Oセット、 S3)へ | Oセット、 S4)へ | セットされていた O出力、 新Oセット、 S3)まま | MO出力、 新Oセット、 S3)へ | 処理B (3キー判定) | セットされていた MO出力、 新Oセット、 S3)へ | 
| 文字キー (M)オフ | - | 当該Mキーオフの場合、 M出力、 S1)へ | - | 処理C (重なり厚み判定) | セットされていたMO出力、S1)へ | セットされていた MO出力、 S1)へ | 
| 親指(O)オフ | - | - | 当該Oオフの場合、 O出力、 S1)へ | 処理F (重なり厚み判定) | 処理D (重なり厚み判定) | - | 
| タイムアウト $T_{th}$ | -- | M出力、 S1)へ | - | MO出力、 S1)へ | MO出力、 S1)へ | セットされていた O出力、 M出力、 S1)へ | 
紅皿の各状態間のタイミングチャート
S2)文字キーオン状態における初期化
この図は、文字キーオン状態における初期化を示します。文字キー$M$が押下されたとき、すぐさま単独打鍵$M$をアプリケーションに出力します。そして同時打鍵の判定時間($T_{th}$)よりも前に修飾キー$X$が押下されたとき、単独打鍵$M$の出力を確定します。
S2)文字キーオン状態における他の文字キーの押下
 この図は、文字キー$M_1$オン状態における他の文字キー$M_2$の押下を示します。文字キー$M_1$が押下されたとき、紅皿は、すぐさま単独打鍵$M_1$をアプリケーションに出力します。
 そして同時打鍵の判定時間($T_{th}$)よりも前に他の文字キー$M_2$が押下されたとき、単独打鍵$M_1$の出力を確定します。
S2)文字キーオン状態における親指キーの押下
 この図は、文字キー$M$オン状態における右親指キー$R$ の押下を示します。
 文字キー$M$ が押下されたとき、紅皿は、すぐさま単独打鍵$M$をアプリケーションに出力します。そして同時打鍵の判定時間($T_{th}$)よりも前に右親指キー$R$が押下されたとき、S4)文字キー親指キーオン状態に遷移します。このとき、タイムアウト時間$T_{MO}$が設定されます。
 この図では、タイムアウト時間$T_{MO}$が経過する前に修飾キー$X$が押下されているので、紅皿は、Backspaceキーで単独打鍵$M$を取り消したのち、同時打鍵$MR$を出力しています。
S2)文字キーオン状態における、当該文字キーのオフ
 この図は、文字キー$M$オン状態における当該文字キー$M$のオフを示します。
 文字キー$M$が押下されたとき、紅皿は、すぐさま単独打鍵$M$をアプリケーションに出力します。そして同時打鍵の判定時間($T_{th}$)よりも前に、この文字キー$M$がオフされたとき、単独打鍵$M$の出力を確定します。
S2)文字キーオン状態におけるタイムアウト
 この図は、文字キー$M$オン状態におけるタイムアウトを示します。
 文字キー$M$が押下されたとき、紅皿は、すぐさま単独打鍵$M$をアプリケーションに出力します。そして同時打鍵の判定時間($T_{th}$)が経過したとき、単独打鍵$M$の出力を確定します。
S3)親指キーオン状態における初期化
親指キー$O$が押下されたとき、紅皿は何も出力しません。そして修飾キー$X$が押下されたとき、親指キーのコード$O$を出力します。
S3)親指キーオン状態における文字キーの押下
 親指キー$O$が押下されたとき、紅皿は何も出力しません。そして文字キー$M$が押下されたとき、同時打鍵$MO$を出力し、かつタイムアウト時間$T_{OM}$を設定して、(S5)親指キー文字キーオン状態に遷移します。
 この図では、(S5)親指キー文字キーオン状態に遷移したのち、修飾キー$X$の押下により同時打鍵$MO$を確定したのち、初期状態に遷移しています。
S3)親指キーオン状態における他の親指キーの押下
右親指キー$R$が押下されたとき、紅皿は何も出力しません。そして左親指キー$L$が押下されたとき、右親指キーのコード$R$を出力します。紅皿において、右親指キーは変換キーに割り当てられていることが多いので、その場合には変換キーのコードを出力します。
S3)親指キーオン状態における当該親指キーのオフ

 親指キー$O$が押下されたとき、紅皿は何も出力しません。そして、当該親指キー$O$がオフされたとき、親指キーのコード$O$を出力して、(S1)初期状態に遷移します。
S4)文字キー親指キーオン状態における初期化

 文字キー$M$が押下されたのち、同時打鍵の判定時間$T_{TH}$以内に親指キー$R$が押下されると、紅皿は、タイムアウト時間$T_{MO}$を設定して(S4)文字キー親指キーオン状態に遷移します。タイムアウト時間$T_{MO}$は、文字キー$M$が押下されてから親指キー$R$が押下されるまでの時間です。
 文字キー親指キーオン状態において、タイムアウト時間$T_{MO}$が経過する前に修飾キー$X$が押下されると、紅皿は、Backspaceキーで単独打鍵$M$を取り消したのち、同時打鍵$MR$を出力します。
S4)文字キー親指キーオン状態における他の文字キーの押下
 文字キー$M_1$が押下されたのち、同時打鍵の判定時間$T_{TH}$以内に親指キー$O$が押下されると、紅皿は、タイムアウト時間$T_{MO}$を設定して(S4)文字キー親指キーオン状態に遷移します。
 文字キー親指キーオン状態において、タイムアウト時間$T_{MO}$が経過する前に他の文字キー$M_2$が押下されると、紅皿は、単独打鍵$M_1$を確定させたのち、同時打鍵$M_2O$を出力します(処理A:3キー判定)。その後、紅皿は、単独打鍵$M_2$を出力して(s2)文字キーオン状態に遷移します。
 なお、タイムアウト時間$T_{MO}$が経過すると、紅皿は、以下に示すように、タイムアウトした場合のモード遷移を実行します。
S4)文字キー親指キーオン状態における他の親指キーの押下

 文字キー$M$が押下されたのち、同時打鍵の判定時間$T_{TH}$以内に右親指キー$R$が押下されると、紅皿は、タイムアウト時間$T_{MO}$を設定して(S4)文字キー親指キーオン状態に遷移します。
 文字キー親指キーオン状態において、紅皿は、タイムアウト時間$T_{MO}$が経過する前に左親指キー$L$が押下されると、Backspaceキーで単独打鍵$M$を取り消したのち、同時打鍵$MR$を出力します。
S4)文字キー親指キーオン状態における当該文字キーのオフ

 文字キー$M$が押下されたのち、同時打鍵の判定時間$T_{TH}$以内に親指キー$O$が押下されると、紅皿は、タイムアウト時間$T_{MO}$を設定して(S4)文字キー親指キーオン状態に遷移します。
 文字キー親指キーオン状態において、タイムアウト時間$T_{MO}$が経過する前に当該文字キー$M$がオフされると、紅皿は単独打鍵$M$を確定させます。(処理C:重なり厚み判定)
 なお、タイムアウト時間$T_{MO}$が経過すると、紅皿は、タイムアウトした場合のモード遷移を実行します。
S4)文字キー親指キーオン状態における親指キーのオフ
 文字キー$M$が押下されたのち、同時打鍵の判定時間$T_{TH}$以内に親指キー$O$が押下されると、紅皿は、タイムアウト時間$T_{MO}$を設定して(S4)文字キー親指キーオン状態に遷移します。
 文字キー親指キーオン状態において、タイムアウト時間$T_{MO}$が経過する前に当該親指キー$O$がオフされると、紅皿は単独打鍵$M$を確定させます。(処理F:重なり厚み判定)
 なお、タイムアウト時間$T_{MO}$が経過すると、紅皿は、タイムアウトした場合のモード遷移を実行します。
S4)文字キー親指キーオン状態におけるタイムアウト
 文字キー$M$が押下されたのち、同時打鍵の判定時間$T_{TH}$以内に親指キー$O$が押下されると、紅皿は、タイムアウト時間$T_{MO}$を設定して(S4)文字キー親指キーオン状態に遷移します。
 文字キー親指キーオン状態において、タイムアウト時間$T_{MO}$が経過すると、紅皿は、Backspaceキーで単独打鍵$M$を取り消して同時打鍵$MO$を出力します。
S5)親指キー文字キーオン状態における初期化

 親指キー$O$が押下されたのち、同時打鍵の判定時間$T_{TH}$以内に文字キー$M$が押下されると、紅皿は、同時打鍵$MO$をアプリケーションに出力してタイムアウト時間$T_{OM}$を設定し、(S5)親指キー文字キーオン状態に遷移します。
 親指キー文字キーオン状態において、タイムアウト時間$T_{OM}$が経過する前に修飾キー$X$が押下されると、紅皿は同時打鍵$MR$を確定させて、(S1)初期状態に遷移します。
S5)親指キー文字キーオン状態における他の文字キー押下
 親指キー$O$が押下されたのち、同時打鍵の判定時間$T_{TH}$以内に文字キー$M_1$が押下されると、紅皿は、同時打鍵$M_1O$をアプリケーションに出力してタイムアウト時間$T_{OM}$を設定し、(S5)親指キー文字キーオン状態に遷移します。
 親指キー文字キーオン状態において、タイムアウト時間$T_{OM}$が経過する前に、他の文字キー$M_2$が押下されると、紅皿は同時打鍵$M_1O$を確定させます。紅皿は、単独打鍵$M_2$を出力して(S2)文字キーオン状態に遷移します。
S5)親指キー文字キーオン状態における他の親指キー押下

 右親指キー$R$が押下されたのち、同時打鍵の判定時間$T_{TH}$以内に文字キー$M$が押下されると、紅皿は、同時打鍵$MR$をアプリケーションに出力してタイムアウト時間$T_{RM}$を設定し、(S5)親指キー文字キーオン状態に遷移します。
 親指キー文字キーオン状態において、タイムアウト時間$T_{RM}$が経過する前に、左親指キー$L$が押下されると、紅皿は同時打鍵$M_R$を確定させ、(S3)親指キーオン状態に遷移します。(処理B:3キー判定)

 なお、タイムアウト時間$T_{RM}$が経過すると、紅皿は、タイムアウトした場合のモード遷移を実行します。
 この処理については、紅皿 ver.0.1.3.4 において、未だ仕様が確定していませんでした。暫定仕様は、親指キー$O$が押下されたのち、文字キー$M$が押下されると同時打鍵$MO$を出力し、その後に別の親指キーが押下されても無視します。
S5)親指キー文字キーオン状態における当該文字キーのオフ

 親指キー$O$が押下されたのち、同時打鍵の判定時間$T_{TH}$以内に文字キー$M$が押下されると、紅皿は、同時打鍵$MO$をアプリケーションに出力してタイムアウト時間$T_{RM}$を設定し、(S5)親指キー文字キーオン状態に遷移します。
 親指キー文字キーオン状態において、タイムアウト時間$T_{RM}$が経過する前に、当該文字キー$M$がオフされると、紅皿は同時打鍵$RM$を確定させ(S1)初期状態または(3)親指キーオン状態に遷移します。
 なお、タイムアウト時間$T_{RM}$が経過すると、紅皿は、タイムアウトした場合のモード遷移を実行します。
S5)親指キー文字キーオン状態における当該親指キーのオフ
 親指キー$O$が押下されたのち、同時打鍵の判定時間$T_{TH}$以内に文字キー$M$が押下されると、紅皿は、同時打鍵$MO$をアプリケーションに出力してタイムアウト時間$T_{OM}$を設定し、(S5)親指キー文字キーオン状態に遷移します。
 親指キー文字キーオン状態において、タイムアウト時間$T_{OM}$が経過する前に、当該親指キー$O$がオフされると、紅皿は(S6)親指キー文字キーオン後親指キーオフ状態に遷移します。(処理D:重なり厚み判定)
 なお、タイムアウト時間$T_{MO}$が経過すると、紅皿は、タイムアウトした場合のモード遷移を実行します。
S5)親指キー文字キーオン状態におけるタイムアウト
 右親指キー$R$が押下されたのち、同時打鍵の判定時間$T_{TH}$以内に文字キー$M$が押下されると、紅皿は、同時打鍵$MR$をアプリケーションに出力してタイムアウト時間$T_{RM}$を設定し、(S5)親指キー文字キーオン状態に遷移します。
 親指キー文字キーオン状態において、タイムアウト時間$T_{RM}$が経過すると、同時打鍵$MR$を確定して(3)親指キーオン状態に遷移します。
S6)親指キー文字キーオン後親指キーオフ状態における初期化
 親指キー$O$と文字キー$M$とが押下され、更に当該親指キー$O$がオフされると、紅皿は、タイムアウト時間$T_{Mo}$を設定して(S6)親指キー文字キーオン後親指キーオフ状態に遷移します。
 親指キー文字キーオン後親指キーオフ状態において、タイムアウト時間$T_{Mo}$が経過する前に修飾キー$X$が押下されると、紅皿は同時打鍵$MO$を確定して(S1)初期状態に遷移します。
S6)親指キー文字キーオン後親指キーオフ状態における他の文字キーの押下
 親指キー$O$と文字キー$M_1$とが押下され、更に当該親指キー$O$がオフされると、紅皿は、タイムアウト時間$T_{Mo}$を設定して(S6)親指キー文字キーオン後親指キーオフ状態に遷移します。
 親指キー文字キーオン後親指キーオフ状態において、タイムアウト時間$T_{Mo}$が経過する前に他の文字キー$M_2$が押下されると、紅皿は同時打鍵$M_1O$を確定して、単独打鍵$M_2$をアプリケーションに出力して(S2)文字キーオン状態に遷移します。
S6)親指キー文字キーオン後親指キーオフ状態における親指キーの押下
 右親指キー$R$と文字キー$M$とが押下され、更に当該右親指キー$R$がオフされると、紅皿は、タイムアウト時間$T_{Mo}$を設定して(S6)親指キー文字キーオン後親指キーオフ状態に遷移します。
 親指キー文字キーオン後親指キーオフ状態において、タイムアウト時間$T_{Mo}$が経過する前に左親指キー$L$が押下されると、紅皿は同時打鍵$MR$を確定して(S3)親指キーオン状態に遷移します。
S6)親指キー文字キーオン後親指キーオフ状態における当該文字キーのオフ
 親指キー$O$と文字キー$M$とが押下され、更に当該親指キー$O$がオフされると、紅皿は、タイムアウト時間$T_{Mo}$を設定して(S6)親指キー文字キーオン後親指キーオフ状態に遷移します。
 親指キー文字キーオン後親指キーオフ状態において、タイムアウト時間$T_{Mo}$が経過する前に当該文字キー$M$がオフされると、紅皿は同時打鍵$MO$を確定して(S1)初期状態に遷移します。
S6)親指キー文字キーオン後親指キーオフ状態におけるタイムアウト
 親指キー$O$と文字キー$M$とが押下され、更に当該親指キー$O$がオフされると、紅皿は、タイムアウト時間$T_{Mo}$を設定して(S6)親指キー文字キーオン後親指キーオフ状態に遷移します。
 親指キー文字キーオン後親指キーオフ状態において、タイムアウト時間$T_{Mo}$が経過すると、紅皿はBackspaceキーで同時打鍵$MO$を取り消したのち、単独打鍵$O$と単独打鍵$M$とを出力して(S1)初期状態に遷移します。
高精度タイマーによる時間測定
 当初は、上記のタイミングチャートをAutohotkeyのタイマー変数を用いて計測していましたが、タイマー変数は約16mSECの精度しかなく、高速に打鍵する場合には精度が不足します。
 よって、以下に示す高精度タイマー関数を作成しました。
 最初にPf_Init()を1度だけ呼び出したのち、Pf_Countを呼び出すと、起動後の経過時間をミリ秒単位で測定可能です。
;*****************************************************************************
;  高精度タイマー関数群 (PfCount.ahk)
;
;	グローバル変数 : TickFrequency, Ticks0
;	使い方:Pf_Init()を呼び出したのちに Pf_Count() を呼び出す
;   AutoHotkey:     L 1.1.29.01
;    Language:       Japanease
;    Platform:       NT系
;    Author:         Kenichiro Ayaki
;*****************************************************************************
;-----------------------------------------------------------
; Performance Counterの初期化
;   戻り値          1:成功 / 0:失敗
;-----------------------------------------------------------
Pf_Init()
{
	global TickFrequency, global Ticks0
	TickFrequency := 0, Ticks0 := 0
	ret := DllCall("QueryPerformanceFrequency","Int64*",TickFrequency) ;obtain ticks per second
	if(ret)
	{
		ret := DllCall("QueryPerformanceCounter","Int64*",Ticks0) ;obtain the performance counter value
	}
	
	if(!ret)
	{
		TickFrequency := 0, Ticks0 := 0
	}
	return ret
}
;-----------------------------------------------------------
; Performance Counterによる起動後の経過時間(ミリ秒)
;   戻り値	0以外 起動後の経過時間 / 0:失敗
;-----------------------------------------------------------
Pf_Count()
{
	global TickFrequency, global Ticks0
	if(TickFrequency = 0)
	{
		return A_Tickcount
	}
	Ticks1 := 0
	If !DllCall("QueryPerformanceCounter","Int64*",Ticks1) ;obtain the performance counter value
	{
		return 0
	}
	myTick := (Ticks1 - Ticks0)*1000.0/TickFrequency
	iTick := Floor(myTick)
	return iTick
}
零遅延モードの課題
 ・打鍵を「コマンド」として用いているアプリケーションでは、Backspaceでコマンドを取り消すことができないので、上手く動作しないおそれがあります。これを回避するため、紅皿では、IMEがローマ字モードであり、かつ、当該文字キーが「親指シフト無し」面と、「右親指シフト」面と、「左親指シフト」面のそれぞれで打鍵内容が異なる場合に限って零遅延モードを有効にしています。なお、「親指シフト無し」面と「右親指シフト」面と「左親指シフト」面の打鍵内容が共通の場合には、シフト状態で打鍵内容が変化しないので、即時に単独打鍵をおこないます。
 ・現在の紅皿は、零遅延モードにおけるBackspaceは1回に固定です。よって、1つのキーに2文字以上を登録すると、先行出力した打鍵のうち2文字目以降が取り消せなくなります。例えば、何れかのキーに’kyaを登録すると、IMEによって2文字の「きゃ」に変換されます。これをBackspaceで取り消した場合、「き」が消え残ります。
 ・制御コードはBackspaceで取り消すことができません。よって、backspaceを示す「後」、Escapeを示す「逃」などをキー配列に登録すると期待した動作にならない場合があります。




















