昨日不満をぶちまけたRazer Tartarus V2だが、悪いのはソフトであって、ハードではない。
ソフトを憎み、ハードを憎まず。
ハードは物がなければどうにもならないが、ソフトなら何とかならないこともないはずだ。
Razer Tartarus V2 メカメンブレン 左手用キーパッド 【日本正規代理店保証品】 RZ07-02270100-R3M1
- 出版社/メーカー: Razer Inc.
- 発売日: 2017/12/23
- メディア: Personal Computers
- この商品を含むブログを見る
HID macros
ここでまず候補に挙がるのが、HID macrosだろう。(※AutoHotkeyはLowLevelKeyboardProcによるフックで動くので、デバイスごとの設定はできない。)
だが、ここで問題が。私のWindows 10ではHID macrosが動かないのだ。
もう開発されていないし、メンテナンスもされていないので仕方がないが、何故だ。
HidKeySequence
HidKeySequenceという複数のデバイスを認識し、それぞれにマクロを割り当てるソフトがあるらしい。
これだ!と勇み足でダウンロードボタンを押すが、私がアクセスブロックされて、ダウンロード出来ない。何故だ。
私は何か悪さをしたのだろうか。
人から拒否されるなんて、そんな…思い当たる節が多すぎるが、HidKeySequenceは昨日知ったのでまだ悪さはしてないと信じたい。
公式はウンコ。AutoHotkeyは駄目。HID macrosは動かない。HidKeySequenceは拒否された。
残念だ。四面楚歌だ。ここで試合終了です。
パンがないならパンを焼けば良いじゃない
と、普通ならもう諦めて、大人しくTartarus V2(1万円)をそっとゴミ箱に捨てるか、Razer synapse3と共に生きる覚悟を決めるのだろうが、残念だが私は糞ニートだ。
ここで我を通せぬ様では糞ニートなんてやっていられない。こんなところで折れる様では、老後が心配になってしまうではないか。親をも恐れぬ精神で立ち向かう義務がある。
さて、長い前置きはここまでにして、もうこうなってしまった以上、自分でプログラムするしかない。
が、ここでも問題がある。
Windowsにおいて、キーボード入力をフックするにはLowLevelKeyboardProcを使えばいいのだが、これだとAutoHotkeyと同様でデバイスを認識出来ない。デバイスを認識したいならRaw Inputを使う必要がある。
なら、これら二つを組み合わせれば良い。これは完全に正しい。これらの情報のどちらが先に来るのか分からない、という点を除けば。
正確に認識するには入力をバッファリングやブロックしたりと、かなり面倒だ。
だったら、もう直接デバイスの入力を奪ってしまえば良いのだが、これをするにはドライバを実装する必要がある。
これも面倒だ。
Interception
流石にドライバは書きたくないし、WinAPIも叩きたくない。
どうにかならないかと探していたら、Interception(GitHub)という、マウスやキーボードの入力を傍受し、書き換えたり送信したりが簡単に出来るCライブラリを見つけた。
仕組みとしてはInterceptionが提供する仮想ドライバが各マウスやキーボードの入力を制御する機能を提供し、ライブラリ利用者はその仮想ドライバを操作する。
なお、ドライバは商用版ではソースコードが提供される様だが、フリー版では非公開だ。
ドライバが安全かどうかは信じて使うしかない。暫く通信を監視してたけど、怪しげな通信は無かったと思うよ。(もし、この記事を真似て自作する場合は、自己責任でお願いします。)
さて、先ずはRazer Tartarus V2の入力をすべて奪ってみる。(なお、HIDは環境に合わせて変更してしてください。)
#include "interception.h" #include <windows.h> #include <iostream> #include <wchar.h> using namespace std; //環境に合わせて変更してください。 const wchar_t KEYBD_HID[] = L"HID\\VID_1532&PID_022B&REV_0200&MI_00"; const wchar_t MOUSE_HID[] = L"HID\\VID_1532&PID_022B&REV_0200&MI_02"; int main(){ // 高優先度化 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); //Interceptionのインスタンス生成 auto context = interception_create_context(); if(!context)return 1; interception_set_filter(context, interception_is_keyboard, INTERCEPTION_FILTER_KEY_DOWN | INTERCEPTION_FILTER_KEY_UP | INTERCEPTION_KEY_E0| INTERCEPTION_KEY_E1); interception_set_filter(context, interception_is_mouse, INTERCEPTION_MOUSE_WHEEL| INTERCEPTION_FILTER_MOUSE_MIDDLE_BUTTON_DOWN| INTERCEPTION_FILTER_MOUSE_MIDDLE_BUTTON_UP ); // Tartarus V2を探す InterceptionDevice keyboard=INTERCEPTION_MAX_DEVICE,mouse=INTERCEPTION_MAX_DEVICE; wchar_t buf[500]; for(size_t i = 0;i<INTERCEPTION_MAX_KEYBOARD;i++){ InterceptionDevice d = INTERCEPTION_KEYBOARD(i); if(interception_get_hardware_id(context, d,buf, sizeof(buf)) && wcscmp(KEYBD_HID,buf)==0){ keyboard = d; break; } } for(InterceptionDevice i = 0;i<INTERCEPTION_MAX_MOUSE;i++){ InterceptionDevice d = INTERCEPTION_MOUSE(i); if(interception_get_hardware_id(context, d, buf,sizeof(buf)) && wcscmp(MOUSE_HID,buf)==0){ mouse = d; break; } } if(keyboard == INTERCEPTION_MAX_DEVICE || mouse ==INTERCEPTION_MAX_DEVICE){ interception_destroy_context(context); return 1; } // 入力を処理する InterceptionDevice device; InterceptionStroke stroke; while(interception_receive(context, device = interception_wait(context), &stroke, 1)>0){ if(device == keyboard){ InterceptionKeyStroke& s = *(InterceptionKeyStroke *)&stroke; cout << "Keyboard Input " << "ScanCode="<< s.code << " State="<< s.state<<endl; }else if(device == mouse){ InterceptionMouseStroke& s = *(InterceptionMouseStroke *)&stroke; cout << "Mouse Input" << " State="<<s.state << " Rolling="<<s.rolling << " Flags="<<s.flags << " (x,y)=("<<s.x<<","<<s.y<<")" << endl; }else{ //他のデバイスの入力は通過させる interception_send(context, device, &stroke, 1); if(interception_is_keyboard(device)){//Escapeで終了 InterceptionKeyStroke &s = *(InterceptionKeyStroke *) &stroke; if(s.code == 1) break; } } } interception_destroy_context(context); return 0; }
これを以下のコマンドでコンパイル。
g++ main.cpp -linterception -Ldllのあるディレクトリ
生成されたa.exeを実行してみる。(dllは読み込める様にしておくこと)
おぉ、Tartarus V2の入力部分は、「Keyboard Input~~」、「Mouse Input~~」と出るが、他のキーボードで「hokanoki-bo-do(他のキーボード)」と打ったところはそのまま出力されている。
確かにTartarusの入力を認識し、その入力を奪うことが出来た様だ。
入力が奪えたなら、今度は入力を変更してみる。Tartarusの1キーが押されたら「nodamusi」、2キーが押されたら「Ctrl+Z×3」を入力する様にしてみる。
if(device==keyboard)の中に以下を追加。
if(s.code == 2) { // 1キー s.code = 49;//n interception_send(context, device, &stroke, 1); s.code = 24;//o interception_send(context, device, &stroke, 1); s.code = 32;//d interception_send(context, device, &stroke, 1); s.code = 30;//a interception_send(context, device, &stroke, 1); s.code = 50;//m interception_send(context, device, &stroke, 1); s.code = 22;//u interception_send(context, device, &stroke, 1); s.code = 31;//s interception_send(context, device, &stroke, 1); s.code = 23;//i interception_send(context, device, &stroke, 1); }else if(s.code == 3){ // 2キー s.code = 0x1D;//CTRL interception_send(context, device, &stroke, 1); s.code = 44;// z interception_send(context, device, &stroke, 1); if(s.state == 0){//キーを押した場合は更に追加で2回押す s.code = 0x1D;//CTRL interception_send(context, device, &stroke, 1); s.code = 44;// z interception_send(context, device, &stroke, 1); s.code = 0x1D;//CTRL interception_send(context, device, &stroke, 1); s.code = 44;// z interception_send(context, device, &stroke, 1); } }
で、試しに実行してみる。(1を押す→Enter→1を押す→2を押す)
おぉおお!
入力を奪えて、異なる入力を突っ込むことが出来た。もう何も怖くない。
ちなみに、このプログラムのメモリ使用量は600kbだ。-O3 -Wl,--subsystem,windows -mwindowsをオプションに入れてコンパイルすると300kb程度になった。ドライバがどの程度のメモリを使ってるか分からないが、Razer synapse3の360Mbと比べるべくもないだろう。変な情報も送信しないし。
むろん、よく出来たソフトに比べれば、直にCを書くのは面倒くさい。ソフトごとにキーバインドを変えたければ、それように作り、手動で起動、終了をするか、アプリケーションを自動認識する機能を作る必要がある。ちょっと面倒だ。
だが、ドライバを書いたりWindows APIを直に弄るより圧倒的に楽だし、C言語だからやりたい放題だ。
例えば、他の矢印ボタンが押されてる間は、他の矢印ボタンは無効化とか、最初の矢印ボタンに変更なんてことも可能だ。
とりあえず、これで快適なRazer Tartarus V2環境を手に入れることが出来た。
Razer Tartarus V2は今、神となったのだ(`・д´・ )