Enterで送信されてしまうアプリを強制的にCtrl+Enterで送信にする方法【Autohotkey v2】

Autohotkey

DiscordやClaudeなどの一部のアプリは、Enterを押すと送信されてしまいます。
これが非常にストレスで、私だけでなく、Twitterを軽く調べてもEnter送信にキレる阿鼻叫喚が見られます。

「Ctrl+Enterで送信する設定を付けろ!」という文句を言い続けて何年経ったか分かりませんが、Enterでしか送信を受け付けてくれないソフトは一向に消える気配がありません。
そのため、様々な試行錯誤の結果、「Enterで送信するソフトを、Enterで改行、Ctrl+Enterで送信にする」ソフトを開発しました。Githubからダウンロードできます。(プレリリースです)

Releases · tomisuke/ctrl-enter-to-send
Contribute to tomisuke/ctrl-enter-to-send development by creating an account on GitHub.

素のahkスクリプトもあります。ahk v2の動作環境がある方はそちらの方がおすすめです。

まだ動作テストが足りないので、信用しすぎないようにしてほしいです。不具合やクラッシュが原因で誤って送信してしまっても責任は取れないので、自己責任でお願いします。

以上なのですが、これを作るのに何回も壁にぶち当たり、それをどうにか発想でクリアしてきたので、その備忘録を以下につらつらと書きます。

Enter と Shift+Enter, Ctrl+Enterを入れ替えるだけじゃだめ

最初はこんな問題を解決するのは簡単で、単にEnterを入力したらShift+Enter(改行)を出力、Ctrl+Enter入力したらEnter(送信)を出力するAutohotkeyスクリプトを用意すれば解決や!と思っていました。
母語が英語ならこれで良かった。

しかし、我々の母語は日本語。入力には変換が必要です。
これがくせ者で、変換を確定するには、Enterを押す必要があります。
しかし、確定しようとEnterを押すとShift+Enterが送信されてしまう。 これでは確定できません。
つまり、変換中であるか否かを区別し、変換中にEnterを押すとEnterを送信。変換後にEnterを押すとShift+Enterを送信する必要があるということです。

IMEを制御する関数群を使う

2012年に、eamat氏がIMEを制御する関数群を公開してくれています。その関数の中に、IME_GetConverting()という、IMEの文字入力の状態を返してくれる関数がありました。
変換中なら1、そうでないなら0を返してくれる仕様です。
Enterを押したとき、もし1だったらEnter(確定)、0だったらShift+Enter(改行)を出力で解決というわけです。

しかし、この関数、完璧ではない。
この状態(状態Aとする)なら正常に1を返してくれるのですが、、、
Pasted image 20260717115831.png
次のように、変換候補のポップアップが出ていない状態での変換中だと、0を返してくるのです。
Pasted image 20260717121306.png
この状態(状態Bとする)になると、Enterを出力しないと確定できないのに、判定は変換後なので、Shift+Enterが出力されることになります。
解決ならず。

状態Bになる操作が成されたらフラグを立てる

状態Aの時にスペースや、各Fnキーを押すと、状態Bになります。そこで、次の処理をします。

Space::
F2::
F3::
F4::
F5::
F6::
F7::
F8::
F9::
F10::
F11::
{
	if(IME_GetConverting()){
		IMEFlag := true
	}
}
Enter::{
	if(IME_GetConverting() OR IMEFlag){
		Send Enter ;Enterを送信
		IMEFlag := false
	}else{
		Send +Enter ;Shift+Enterを送信
	}
}
^Enter::Enter ;Ctrl+EnterでEnterを送信

mermaid.png
状態Bに遷移するキーが押された時に、フラグ(IMEFlag)をTrueにすることで、状態Bを認識することができると考えました。

IME_GetConvertingまたはIMEFlagの時に変換中と判断すればいいのです。
実際、これは上手くいきました。
これで完成したかに思えました。実際に何の問題もなく3日間くらい使っていました。
しかし、しかし、、、まだ穴がありました。文字入力をしようと子音のキーを押したとき、以下の状態になります。
Pasted image 20260717133520.png

この後母音キーをおせば、ひらがなが完成し、変換中の判定になります。しかし、この子音だけで、ひらがなが完成していないとき、IME_GetConverting()は、変換中でないと判断するのです。この状態を状態Cとします。

状態Cのとき、事前にSpaceやFnキーは押されていないので、IMEFlagもFalseのままです。よって、変換中でないと判断し、状態CでEnterを押すとShift+Enterが出力されてしまいます。実際は変換中のため文字は確定されず、改行も行われません。

これは詰んだのではないかと思いました。テキストボックスにいきなり何か文字が打たれた時点で状態Cになるわけですから、事前の目印がありません。

InputHookを使う

もう諦めて、状態Cになっちゃったら、消すかひらがなにするかCtrl+Enterで確定させるかで誤魔化してたのですが、ある日InputHookというAutohotkeyの機能に出会いました。
InputHookはキーボード入力をリアルタイムで捕捉・監視するためのオブジェクトです。これを使えば、入力される全てのキーを補足することができます。
これを使えば、状態Cを認識できると考えました。

作戦としては、InputHookで入力キーを監視し、状態Cになりうるキー(文字キーや記号キー)が入力されたら、IMEFlagをTrueにするというものです。

しかし、この方法では文字を入力していない、通常のPC操作時にも反応してしまい、思わぬ動作を招く可能性があります。そこで、Descolada氏のUIAライブラリを使用し、テキストボックスにフォーカスしている状態か否かを判定する関数isTextBoxFocused()を作成しました。

isTextBoxFocused() {
    try {
        el := UIA.GetFocusedElement()
        ctrlType := el.CurrentControlType
        return (ctrlType = 50004) OR (ctrlType = 50030) OR (ctrlType = 50026 AND el.IsTextEditPatternAvailable)
    } catch {
        return false
    }
}

さらに、状態Cになる対象のキーかを判定する関数isTextGeneratingKey()を作成しました。この関数の設計は、文字を生成しないキーを知る限り全て並べ、該当しなかったらTrueを返すというものです。

isTextGeneratingKey(vqk) {
    static NonTextKeys := Map(
        ;マウスボタン
        0x01, true, 0x02, true, 0x04, true, 0x05, true, 0x06, true,
        ;制御・特殊キー
        0x08, true, ; BackSpace
        0x09, true, ; Tab
        0x0C, true, ; Clear
        0x0D, true, ; Enter
        0x13, true, ; Pause
        0x14, true, ; CapsLock
        0x1B, true, ; Escape
        ;IME制御
        0x15, true, ; Kana/IME_ON
        0x17, true, ; Junja
        0x18, true, ; Final
        0x19, true, ; Kanji/Hanja
        0x1A, true, ; IME_OFF
        0x1C, true, ; Convert-変換
        0x1D, true, ; NonConvert-無変換
        0x1E, true, ; Accept
        0x1F, true, ; ModeChange
        ;ナビゲーション
        0x21, true, 0x22, true, 0x23, true, 0x24, true, ; PageUp/Down/End/Home
        0x25, true, 0x26, true, 0x27, true, 0x28, true, ; 矢印
        0x29, true, ; Select
        0x2A, true, ; Print
        0x2B, true, ; Execute
        0x2C, true, ; PrintScreen
        0x2D, true, 0x2E, true, ; Insert, Delete
        0x2F, true, ; Help
        ;Windows、Apps
        0x5B, true, 0x5C, true, 0x5D, true, ; LWin, RWin, Apps
        0x5F, true, ; Sleep
        ;Fn
        0x70, true, 0x71, true, 0x72, true, 0x73, true, 0x74, true, 0x75, true,
        0x76, true, 0x77, true, 0x78, true, 0x79, true, 0x7A, true, 0x7B, true,
        0x7C, true, 0x7D, true, 0x7E, true, 0x7F, true, 0x80, true, 0x81, true,
        0x82, true, 0x83, true, 0x84, true, 0x85, true, 0x86, true, 0x87, true,
        ;ロック系
        0x90, true, 0x91, true, ; NumLock, ScrollLock
        ;修飾キー
        0x10, true, 0xA0, true, 0xA1, true, ; Shift系
        0x11, true, 0xA2, true, 0xA3, true, ; Ctrl系
        0x12, true, 0xA4, true, 0xA5, true, ; Alt系
        ;ブラウザ系
        0xA6, true, 0xA7, true, 0xA8, true, 0xA9, true,
        0xAA, true, 0xAB, true, 0xAC, true,
        ;メディア・音量系
        0xAD, true, 0xAE, true, 0xAF, true,
        0xB0, true, 0xB1, true, 0xB2, true, 0xB3, true,
        0xB4, true, 0xB5, true, 0xB6, true, 0xB7, true,
        ;IME関連(拡張)
        0xE5, true, ; VK_PROCESSKEY (IME処理中のキー、環境依存)
        ;その他システム系
        0xF6, true, 0xF7, true, 0xF8, true, 0xF9, true,
        0xFA, true, 0xFB, true, 0xFC, true, 0xFD, true, 0xFE, true
    )
    return !NonTextKeys.Has(vk)
}

ゴリ押しですが仕方ありません。漏れがあったら教えてください。

テキストボックスにフォーカスされているかつ、IMEがOn(日本語入力がOn)かつ、isTextGeneratingKeyがTrueの時、状態CとみなしIMEFlagをTrueにします。何かキーが押されるたびに、毎回以下のコードを実行します。

OnIMEFlagInput(ih, vk, sc) {
    global IMEFlag
    if (!IsCtrlEnterTarget())
        return
    if (IMEFlag)
        return
    if (!IsTextBoxFocused())
        return
    if (!IME_GET())
        return
    if (IME_GetConverting() != 0) {
        IMEFlag := true
        return
    }
    if (isTextGeneratingKey(vk)) {
        IMEFlag := true
    }
}

この方法は試行錯誤を繰り返しつつも何とか上手くいき、これまでの方法と合わせて、見事に正しく変換中かどうかを取得できるようになりました。

最終的な設計

一部細かい処理を省き、ざっくりとこのような処理になりました。
mermaid3 2.png
注釈)
= :比較演算子
:= :代入演算子
OR :または
AND :かつ
! :でない

コードを見たい方はレポジトリをご覧ください。不具合やカスコードを見つけたらissueを飛ばして頂けると助かります。適当でいいので。

GitHub - tomisuke/ctrl-enter-to-send
Contribute to tomisuke/ctrl-enter-to-send development by creating an account on GitHub.

何かしらのキーが押されるたびに、IMEが有効かつ、テキストボックスにフォーカスされているかを確認。
もしそうなら、現在IME変換中か(ただし変換中なのに変換中でないと返す場合がある)を確認。変換中ならフラグをTrueにして終了。
変換中でないなら、押されたキーがテキスト入力を行うキーか確認。テキスト入力を行わないキーなら何もせず終了。
行うキーならば、日本語入力中かつ、テキストを入力するキーが押されたことから、変換中とみなし、フラグをTrueにして終了。
という流れです。

まとめ

以上です。ダウンロードして使ってみて、もし不具合、要望があればお知らせください。

SNS一覧・問い合わせ
GitHubアカウントリンクXメインアカウントサブアカウントDiscordアカウントリンクTomisuke配列コミュニティお問い合わせスパムじゃなければなんでもOKです。

再度但し書きですが、このソフトがクラッシュしたり、不具合があったりで、意図せず文章が送られてしまっても責任は取れないので、よろしくお願いします。

ソフトを開発する各企業は一刻も早くCtrl+Enterで送信するオプションを追加してほしいものです。

コメント

タイトルとURLをコピーしました