3キー同時押しの理屈

テストケースが煮詰まってきたので、一応簡単に理屈を書いておきます。

3キー同時押しが発生する場合、押されるキーの数は1→2→3→2→1と変化します。まず1→2→3と昇順で増えていくのを確認するために関数を作成しました。2キー同時押しの場合です。

test()
{
	If GetKeyState("r","P") and GetKeyState("i","P") and !GetKeyState("j","P"){
		return 1
	}
	Else If GetKeyState("r","P") and GetKeyState("j","P") and !GetKeyState("i","P"){
		return 1
	}
	Else If GetKeyState("i","P") and GetKeyState("j","P") and !GetKeyState("r","P"){
		return 1
	}
}

1キーと3キーの場合も条件が違うだけで理屈は同じです。次に降順で減る場合ですが、文字を出力したあとにポーズをかけるしかありませんでした。単にそのままポーズを再開させると余計な出力も行われますので、再開時にはリロードをかける必要があります。

問題はいつリロードをかけるかですが、ポーズの直後や直前だと成功率に影響が出ます。次の単打入力でリロードすることにしました。

r::
KeyWait,r
getString("し")
If(A_IsPaused>0){
	Reload
}
Return

他にもっといいタイミングあればいいのですが、現状はこうしています。

なぜ先行入力キーを意識する必要があるのか

確証があるわけではないので、あくまで仮説です。

3キー同時押しの後に単打を打つとすると押されるキーの数は
→2→3→2→1→0→
と変化します。太字で示した最初と最後のキーが同一であるとAutoHotkeyは同一の処理だとみなすようです。ポーズがかかっている処理と同じ処理を行うわけですから解除されません。0キーの段階で処理を終了したいのですがどうすればいいかわかりません。

追記

フラグ管理を見直した結果先行入力キーを意識せずともなんとか仕様を満たせそうです。上は理屈の説明として残しておきます。

最後のお願い

ここまで薙刀AHK版に挑戦してようやくわかったのは、BackSpaceを使う手法には限界があるようです。どうすれば使わないで済むのでしょう。

その答えに対するアイデアを出すためにまたコードを弄りました。RJI同時押しのテストです。key_press_test.ahkを実行してください。2キー以上同時押し入力の後にPauseをかけます。Space入力でreloadをかけて再開します。
https://drive.google.com/file/d/1QDOetC2mgAk7BmnjbjVQhk5teou7Npt2/view?usp=sharing

実用的ではありませんが、あくまでもアイデアとして残しておきます。

追記

RJ同時押しはI、RI同時押しはJでreloadをかけてPauseから再開できるようにしました。もちろんIMEオンオフの切り替えにこだわる大岡さんがこんな仕様を認めてくださるとは思えません。まだ実用には程遠いですね。
naginata_key_test_0.1.zip - Google ドライブ

追記2

同時押し発生時にメッセージボックスを出して何もしなければ再開するようにしました。単にユーザーの邪魔をしているだけと言われてしまえばそれまでですがそう悪くない気もしています。失敗率は40回に1回ほどです
naginata_key_test_0.1.1.zip - Google ドライブ

追記3

メッセージボックスを廃止してリロード前に入力を受け取らない時間を作りました。もしリロードが完了するまで入力をしないでくだされば成功率はほぼ100%のはずです
naginata_key_test_0.1.2.zip - Google ドライブ

追記4

入力を受け取らない時間をハードコーディングで指定しないようにしました。雑に打って成功率は95%ほどです。そろそろ限界かもしれません。
naginata_key_test_0.1.3.zip - Google ドライブ

追記5

入力可能になるまでの間に、スプラッシュウィンドウを表示しました。小さいウィンドウが表示されているタイミングで入力を行わないでください。
naginata_key_test_0.1.4.zip - Google ドライブ

追記6

同時押し時に最初に入力したキー*1以外の入力で、ポーズを解除するようにしました。0.1無印との違いはポーズがかかっていても最終入力時の先行入力キーと異なれば出力を行います。もし先行入力キーの単打を打ちたい、または確認できなければスペースで解除してください(IMEオフ時のみ)
naginata_key_test_0.1.5.zip - Google ドライブ

追記7

先行入力キーをスプラッシュウィンドウで表示するようにしました。この方式に慣れれば目障りかもしれませんが初心者向けなら悪くない気もします
naginata_key_test_0.1.6.zip - Google ドライブ

追記8

先行入力キーをスプラッシュウィンドウに表示する時間を長めにしました。先行Nと表示されているキーを単打か先行入力キーとして使いたい場合はスペースや別の文字キー単打でポーズを解除してください。
naginata_key_test_0.1.7.zip - Google ドライブ

追記9

単打でスプラッシュウィンドウを表示しないようにしました。そろそろ動作報告がほしいところです
naginata_key_test_0.1.8.zip - Google ドライブ

追記10

先行入力キーを意識しなくともシームレスに入力を可能にしました。ここまでやってきた甲斐がありました。
naginata_key_test_0.1.9.zip - Google ドライブ

追記11

key_press_test.ahkのコードを整理しました。
naginata_key_test_0.1.10.zip - Google ドライブ

*1:仮に先行入力キーと呼びます

薙刀式AHK版を書き直した

ついに完成しました。とはいえバグがあるはずです。見つけた方はお知らせください。
naginata_rapid_0.1.5.zip - Google ドライブ

H絡みの3キー同時押しが一部出力されない環境問題が最大のハードルでした。仕様外の定義を廃止してあくまで大岡さんの仕様を再現するのに徹しています。本家と違う点はqを経由する小書きがqの先押し限定であったり、「ゎ」がSpace+qである程度です。もし気になる方がいるようなら直します。

前回も書きましたがIMEオフ時に動作しますのでやはりWindowsのログインは数字だけでログインできるよう設定を変更しておくことをおすすめします。

3つあるahkファイルからnaginata_rapid.ahkを実行してください。FG同時押し(本家のIMEオフ)で動作を中断、HJ(本家のIMEオン)で再開します。IMEと両方をコントロールするのが難しい方やタイプウェル等のタイピングソフトを打ちたい方は素直に大岡さんのDvorakJ版を使ってください。

追記

漢字変換ができない問題がありますが、IMEオフ時に動かす設計思想がある以上どうにもなりません。こちらのサイトなどにコピペして変換してください
anti.rosx.net

追記2

起動中であるのかどうかわかりにくいのでHJ同時押しで再開した際にメッセージを出すようにしました。「OK」を押さずに開いたままにしておくとウィンドウの有無で起動中なのか確認しやすいはずです。FG同時押しで停止すると閉じます。
naginata_rapid_0.1.6.zip - Google ドライブ

追記3

まだわかりにくいなと思ったので、停止時にもメッセージを出すようにしました。
naginata_rapid_0.1.7.zip - Google ドライブ

追記4

大岡さんに報告していただいたバグを3点修正しました。コメントに書かせていただいた内容を一部こちらにも書いておきます。
naginata_rapid_0.1.8.zip - Google ドライブ
oookaworks.seesaa.net
・シフトのTYはシフト←→ですね。
気が回りませんでした。直します。
・単打「う」が「い」になってる。
単純なミスです。すみません。直します。
・空白文字が出ない。SandSが上手くいっていない?
SandSではなくSpaceを完全に修飾キーとして扱っていました。直します。

追記5

大岡さんからご指摘を頂いた「じょ」の同時押しですが「徐々に」の入力が10回に1回程度の確率で失敗することを確認しました。明らかにもっと高い確率で失敗する方は遠慮なく教えて下さい。

対策はいい考えが浮かびません。やはり使い込んでいる方の感覚は鋭いし、それに応えるのはシビアだなと思うばかりです。

追記6

大岡さんから報告いただいた「じゃ」が出力されないバグを修正しました。RJH同時押しのRを最後に押す場合に出力されないのはご容赦ください。
naginata_rapid_0.1.9.zip - Google ドライブ

追記7

どうにもならないケースを動画にしました。これをやりたい方はDvorakJ版を使ってください。
www.youtube.com

追記8

実験用に試作品を書きました。key_triple.ahkを実行してRJI同時押しを試してください。
naginata_rapid_0.1.9.1.zip - Google ドライブ

成功率は若干上がった気はしますが、Gosubはあまり使いたくない手法です。もし皆さんに試していただいてこれまでと変わらないようならお蔵入りです。

追記9


これに成功したverです。
naginata_rapid_0.1.9.2.zip - Google ドライブ

大岡さんのアドバイス通り、2ミリ秒スリープを入れたら安定した模様です。BSによるちらつきがかなり強いので一部の処理はオプションにすべきかもしれません。

追記10

大岡さんに試していただいたところ、改善効果がなかったそうです。残念ですがお蔵入りです。

テストに付き合ってくださった大岡さんにせめてものお礼として、UWSCでRJI同時押しの順番を確認するスクリプトを書きました。ここまでお付き合いくださった皆さん、ありがとうございました。
RJI.UWS - Google ドライブ

薙刀式AHK版に再びリベンジ

「しゃしゅしょ じゃじゅじょ」まで実装しました。150行ほどです。IMEオフ時のみ動作します。Windowsログイン時のパスワードは数字にしておくことをおすすめします。
https://drive.google.com/file/d/1v_5ledIS1HVkwMVCcJa7Io4H24kSOiGS/view?usp=sharing

ここ数日UWSCを弄った結果、これをやるくらいならAHK版を作り直すべきだという結論に至りました。既存の問題点をまとめます。

1.大岡さんの実装通りに打てないキーが一部存在する
2.長すぎてコードの管理に問題が生じる
3.IMEに縛られず実装するには日本語を直接出力する必要がある

一番大きい問題は1です。コード変更で対応します。

~r::
;(IJ)+R
If GetKeyState("i","P") && GetKeyState("j","P"){
	getString_mae2("じょ")
}

RJI同時押しで最後にRを押す場合のコードです。これまでと比較するとわかりにくいのは問題ですがその分BSの数は安定しそうです。

2はUWSC同様に外部ファイルを使いますが、どこまで削れるかは未知数です。他に問題が生じるようなら使わないかもしれません

3はiniファイルを経由させることにします。クリップボードを経由させる手法を試してみたのですが、入力が安定しなさそうです。書いておいてなんですが、この手法だと直接出力するのとなにがどう違うのかよくわかりません。あまり意味があるとは思えませんがセキュリティに敏感なご時世を考えて対策を取っているポーズだけでもやっておきます。

getString(str){
  IniWrite, %str%, naginata_tmp.ini, tmp, kore
  IniRead, %str%, naginata_tmp.ini, tmp, kore
  Send,%str%
}

追記

なにか変だなと思ったら、Googleトライブのユーザー制限を解除していませんでした。失礼しました。上げ直します。「きゃきゅきょ ぎゃぎゅぎょ」を加えました。
naginata_rapid_0.1.zip - Google ドライブ

ahkファイルが3つ入っています。解凍してすべて同じフォルダにおいてください。書き忘れていましたが、FG同時押しで中断、HJ同時押しで再開します。起動の際はnaginata_rapidを実行するだけで構いません。

追記2

iniファイルを付け忘れていたいたので上げ直しました。内容に変更はありません。
naginata_rapid_0.1.1.zip - Google ドライブ

追記3

今度はime.ahkを忘れていました。再開時絡みのバグを修正しています
naginata_rapid_0.1.2.zip - Google ドライブ

追記4

「ぢゃ」の実装に苦戦しています。原因がわかりません。やはりここが鬼門です。

追記5

もしかしてと思って試したのですが、単に使用しているキーボードがGJH同時押しを認識していなかっただけの話かもしれません。こちらのサイトで動作しているのを確認できませんでした
キーボード同時入力テスト

追記6

サブ機でGJH同時押しの動作を確認しました。ようやく進めます

追記7

外来音を除く拗音の3キー同時押しを定義しました。naginata_rapid.ahkは500行ほどです
naginata_rapid_0.1.3.zip - Google ドライブ

追記8

JとMを経由する3キー同時押しを定義しました。あとは残りの単打面とSpace面だけです
naginata_rapid_0.1.4.zip - Google ドライブ

薙刀式UWSC版の課題

前回の記事で分かった課題は大きく分けて2つです。
1.(おそらく)条件分岐が多岐にわたると、処理が重くなり安定して動作しない
2.複数同時押しと並行して単打を実装するのが難しい

1に関してはコードを複数のファイルへ分割し呼び出す方法である程度解決しそうです。2はこれからです。

追記

IMEオンで同時押しを、IMEオフで単打を出力してはどうかと考えました。AutoHotkeyのホットストリングで実装するとこんな感じです。

#include ime.ahk
#If !IME_GET()
:*?:r::し
:*?:j::あ
:*?:i::る

Space & r::
clipboard = め
Send, ^v
Return
Space & j::
clipboard = の
Send, ^v
Return
~f & g::
~g & f::
Send,!{F1}
IME_SET(1)
Return
#If IME_GET()
~h & j::
~j & h::
Send,!{F2}
IME_SET(0)
Return

メイン側

CALL JYO.uws
CALL JYU.uws
CALL JYA.uws
PUBLIC MAIN_FL=1
PUBLIC RJ_FL=0
WHILE 1
IF GETKEYSTATE(VK_R) and (GETKEYSTATE(VK_I) or GETKEYSTATE(VK_P) or GETKEYSTATE(VK_H)) then
  sleep(0.125)
  thread JYO.JYO_MAIN
  MAIN_FL=0
ELSEIF (GETKEYSTATE(VK_R) and GETKEYSTATE(VK_J)) and !(GETKEYSTATE(VK_I) or GETKEYSTATE(VK_P) or GETKEYSTATE(VK_H)) then
  SENDSTR(GETID(GET_ACTIVE_WIN),"じ")
  RJ_FL=1
  sleep(0.125)
ENDIF

IF GETKEYSTATE(VK_R) and GETKEYSTATE(VK_J) and GETKEYSTATE(VK_I) then
 IF RJ_FL>0
  SENDSTR(GETID(GET_ACTIVE_WIN),"ょ")
  RJ_FL=0
  sleep(0.125)
 ELSEIF MAIN_FL>0
  SENDSTR(GETID(GET_ACTIVE_WIN),"じょ")
  MAIN_FL=0
 ENDIF
ENDIF
WEND

サブ側

CLASS JYO
PROCEDURE JYO_MAIN
PUBLIC RJI_FL=0
PUBLIC RJP_FL=0
PUBLIC RJH_FL=0
PUBLIC RJ_FL=0
PUBLIC RI_FL=0
PUBLIC RP_FL=0
PUBLIC RH_FL=0
PUBLIC TN_FL=0

WHILE GETKEYSTATE(VK_R)
 TN_FL=1
 IF GETKEYSTATE(VK_I) AND GETKEYSTATE(VK_J)
  RJI_FL=1
  RI_FL=0
  sleep(0.125)
  EXIT
 ELSEIF GETKEYSTATE(VK_I)
  RI_FL=1
  sleep(0.25)
 ENDIF
WEND

IF RI_FL>0 then
 SENDSTR(GETID(GET_ACTIVE_WIN),"しょ")
 RI_FL=0
ELSEIF RJI_FL>0 then
 SENDSTR(GETID(GET_ACTIVE_WIN),"じょ")
 RJI_FL=0
ENDIF
FEND
ENDCLASS

薙刀式UWSC版へのステップ

3キー同時押しのテストです。「しょ」と「じょ」が表示されるでしょうか。
oookaworks.seesaa.net

追記

書き直しました。漢字変換ができないという問題にはひとまず目をつぶってください

追記2

また少し書き直しました。忘れていましたがWindowsのサインインをPINに変更しないと最悪再起動の必要がありますので注意してください。

追記3

「しゃしゅしょ じゃじゅじょ」まで書きました。これだけで100行以上使っています

追記4

書き直して「しょ」「じょ」まで戻しました。ようやく成功率、行数ともそれなりに納得できるレベルまで持ってこれました

追記5

「しゃしゅしょ じゃじゅじょ」まで戻しました。単打面はAHKで実装するつもりです

追記6

どうやっても単打面の動作が安定しません。そろそろ限界かもしれません

追記7

また成功率が下がった気がします。手を入れれば入れるほど悪くなる気がします

追記8

また「しょ じょ」まで戻しました。これだけならどうにかなりそうです

UWSC

CLASS JY
PROCEDURE JY_MAIN
PUBLIC RJI_FL=0
PUBLIC RJP_FL=0
PUBLIC RJH_FL=0
PUBLIC RJ_FL=0
PUBLIC RI_FL=0
PUBLIC RP_FL=0
PUBLIC RH_FL=0
PUBLIC TN_FL=0

WHILE GETKEYSTATE(VK_R)
 TN_FL=1
 IF GETKEYSTATE(VK_J) AND GETKEYSTATE(VK_I)
  RJI_FL=1
  RI_FL=0
  TN_FL=0
  EXIT
 ELSEIF GETKEYSTATE(VK_I)
  RI_FL=1
  TN_FL=0
   sleep(1)
 ENDIF
WEND

IF RJI_FL>0 then
 SENDSTR(GETID(GET_ACTIVE_WIN),"じょ")
 RJI_FL=0
ELSEIF RI_FL>0 then
 SENDSTR(GETID(GET_ACTIVE_WIN),"しょ")
 RI_FL=0
ELSEIF TN_FL>0
 SENDSTR(GETID(GET_ACTIVE_WIN),"し")
 TN_FL=0
 sleep(0.875)
ENDIF
FEND
ENDCLASS

AHK

Space::
w::
r::
i::
j::
p::
h::
Return

UWSCとAHKでIME.ahkを使わずにIMEを操作 その2

前回は直接F+GでのIMEオフを実現できませんでした。UWSCのスケジュール設定で特定のウィンドウが起動するタイミングで、同時に指定したスクリプトが起動できるようです。あまり好んで使いたくはない手法ですが、IMEオフの際いったんダミー用のウィンドウを立ち上げることでIMEオフを維持するスクリプトを起動させてはどうかと考えました。

幸いダミー用のウィンドウを立ち上げるフリーソフトがありましたのでこれを使います。
www.vector.co.jp

前回のスクリプトに2箇所あるこの行を

SCKEY(0, VK_ALT, VK_F2)

以下のように変更してください

ID2 =EXEC("C:\(FrameDummyのファイルパス)\FrameDummy.exe")
SCKEY(0, VK_ALT, VK_F2)
CTRLWIN( ID2 , CLOSE)

UWSC本体側の設定も行う必要があります。手順は以下の通りです。

1.Ctrl+Wか起動時に5つあるアイコンの右端をクリックして「設定(X)」を選び、設定画面から「スケジュール設定をする(S)」を選んでください。
2.スケジュール設定で1から14のうちどこでも構いませんので前回の「ホットキー3」のスクリプトを登録してください。
3.2で登録した箇所の右にある「設定なし」ボタンをクリックしてください。
4.タイマー設定をこのようにしてください

5.開いたウィンドウを「OK」ボタンで閉じてください

追記

やはりうまくいかないですね。何がどう役に立つかわかりませんので一応残しておきます