ローカル向けWebアプリでIMEのON/OFFを制御したい
ローカル向けのWebアプリ(入力フォーム)を作成していて、JavaScriptでIMEのON、OFFを制御したいケースがあり対策を考えました。結果、ローカルPCの環境を自由にできて、PCのパフォーマンスの低下を許容するなら、方法を見つけることができました。かなり泥臭いですが、Windows10とGoogle Chrome環境で実現できました。
仕組みとしては、ローカルでWebサーバーを構築し、IMEを制御したいページのJavaScriptからそのサーバーにアクセスします。ローカルWebサーバーではアクセスに応じてIME制御のスクリプトを起動させます。
繰り返しますが、制御できるのはローカルの環境を触れるケースに限ります。他のケースでIMEを制御したいと考えていた方は、すみません。この書き方では実現できません。
IMEの制御
IMEの制御から割と難しい問題でした。キーボードを触れるなら「漢字」キーを押すだけでいいのですが、それをコードを経由してやろうとすると意外に難しいことがわかりました。また「漢字」キーがトグル(押すたびにON/OFFが切り替わる)になっているのも困ります。
なので、まずPC Watch:「今こそすべての日本国民に問うIMEのオンとオフ」に従い、IMEのONキーとOFFキーを設定します。設定にある検索ウインドウで「日本語IME設定」を探し、出てきた画面から「キーとタッチのカスタマイズ」を選択します。ここで、「各キーに好みの機能を割り当てる」という機能をONにして、「無変換キー」に「IME-オフ」、「変換キー」に「IME-ON」を設定しました。
ここで設定したキーをスクリプトから押すことができればいいので、VBS(Visual Basic Script)を用いてたSendKeysを考えました。
しかしVBSのSendKeysで「無変換」や「変換」キーを押す方法がありません。
そこで「Power Toys」を使って、キーの設定を変えます。Power ToysはWindowsを便利に使うユーティリティでGitHubに公開されています。この中の「Keybord Manager」を利用して、SendKeysで利用可能なキーに割り当てます。
Power Toys
Power Toysを先のサイトからダウンロードしてインストール、起動した後に「KeyBoard Manage」を選択します。そこで「ショートカットの再マップ」という項目があるので、クリックします。そこからキーの設定をします。
ブラウザの操作に影響しないキーを選んで設定します。筆者は「Alt+H」と「Alt+M」を選択し、前者を「変換」、後者を「無変換」キーに割り当てました。Typeボタンでダイアログを出せば、実際に利用したいキーを直接押すことで指定することもできます。
後から気づきましたが、先の「キーのタッチとカスタマイズ」の設定をしないでもPowerToysの割り当てで直接「IME-Convert」(ON)や「IME-Non-Convert」(OFF)にすればよさそうだと思って試してみたのですが、「IME-Non-Convert」(OFF)側がうまく機能しませんでした。(ボタンを押すとカタカナ、半角カタカナ、ひらがなの3種のトグルボタンになってしまいます)
またKeyBoard Manageの設定を常用するなら、PowerToysの設定から「起動時に(PowerToysを)実行する」を選択しておく必要があります。
VBScript
VBScriptでは単にSendKeysを実行するだけです。引数によってON、OFF切り替える方法もありますが、別々のファイルにしました。ALTは%、Shiftは+、Ctrlは^で表します。PowerToysでは文字が大文字で表示されますが、つられて大文字にしないようにしましょう。VBScriptのSendKeysでは文字を小文字で設定します。
ime_on.vbs
dim wsh
set wsh = WScript.CreateObject("WScript.Shell")
'ALT+H
wsh.SendKeys "%h"
ime_off.vbs
dim wsh
set wsh = WScript.CreateObject("WScript.Shell")
'ALT+M
wsh.SendKeys "%m"
ファイルを作成したらダブルクリックで意図した挙動になるか確認してください。もしうまくいかなかったら、ALT(Left)をALTやALT(Right)等にしてみて稼働するように調整してください。
ローカルサーバー
IME変更の指示を待ち受けるローカルサーバーはNode.jsで作成します。専用のフォルダを作りnpm init -yを実行した後サーバー用のコードを記述します。Quiita:「node.js 超入門②webサーバを作る(ルーティングもやってみる)」やわくわくBank.「シェルコマンドを実行する方法」を参考にさせていただきました。
CORSの問題に対応するために、ヘッダのAccess-Control-Allow-Originに*(ワイルドカード)を設定しています。
あとはアクセスしてくるアドレスに合わせて、先ほど作成したVBSCriptを実行します。8080は待ち受けポートです。空いてるポートを使用してください。
changeime.js
const http = require('http');
const { execSync } = require('child_process');
http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin','*');
if (req.url==='/on') {
execSync('cscript c:\\ime_on.vbs');
} else if(req.url==='/off') {
execSync('cscript c:\\ime_off.vbs');
}
res.writeHead(200, {'Content-Type' : 'text/plain'});
res.end();
}).listen(8080, () => console.log('server start'));
Webサーバー上の設定
IMEをコントロールしたいサーバーのページでは、IMEをオンにしたいinputまたはtextarea要素には「imeon」クラス、オフにしたい要素には「imeoff」クラスを付与し、focus時のイベントにIME変更の指示となるhttpアクセスを設定します。
TABで高速移動した際に負荷を軽減できるようにsetTimeoutで遅延させています。またこのウェイトは、各入力要素にフォーカスが当たるのを待つ働きも兼ねています。フォーカスが当たる前にSendKeysが実行されると意図した挙動になりません。
スクリプトがアクセスするポートは先に設定したポートと一致させる必要があります。
form.html
...
<script>
let imemode = "on";
let intWait = 500;
let blnBusy = false;
const initElements = ()=> {
let imecontrols = document.getElementsByClassName('imeon');
for (let i = 0; i < imecontrols.length; i++) {
imecontrols[i].onfocus = ()=> {
imemode="on";
if (blnBusy===false) {
blnBusy = true;
setTimeout(intWait,imeChange());
}
};
}
imecontrols = document.getElementsByClassName('imeoff');
for (let i = 0; i < imecontrols.length; i++) {
imecontrols[i].onfocus = ()=> {
imemode="off";
if (blnBusy===false) {
blnBusy = true;
setTimeout(intWait,imeChange());
}
};
}
}
const imeChange=()=> {
blnBusy = false;
let req = new XMLHttpRequest();
let strTargetUrl="http://localhost:8080/"+imemode;
req.open('GET',strTargetUrl,true);
req.send(null);
}
initElements();
</script>
これで、ローカルPC側で「node .¥changeime.js」として、Webサーバーを起動した状態で、ページにアクセスすれば選択された要素に合わせてIMEのON/OFFが自動で切り替わるようになります。
httpでは非推奨
以上でやりたいことは実現できたのですが、以下のような「非推奨」警告が出ていました。2021年7月からGoogle Chromeは「非セキュアコンテンツ」からのプライベートアドレスへの接続をブロックするようです。非セキュアでなければいいということは元のページ(入力フォーム側)をhttpsにすればいいだけの話でいいと判断し、httpsに対応させてみたらメッセージは表示されなくなりました。
ちなみに、別記事ではこのNode.jsのサーバー環境をElectronを使ってアプリ化しています。こちらもあわせてお読みいただければ幸いです。
参考にさせていただきましたサイトの皆様、ありがとうございました。