と、普通ならもう諦めて、大人しく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);
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
);
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)){
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) {
s.code = 49;
interception_send(context, device, &stroke, 1);
s.code = 24;
interception_send(context, device, &stroke, 1);
s.code = 32;
interception_send(context, device, &stroke, 1);
s.code = 30;
interception_send(context, device, &stroke, 1);
s.code = 50;
interception_send(context, device, &stroke, 1);
s.code = 22;
interception_send(context, device, &stroke, 1);
s.code = 31;
interception_send(context, device, &stroke, 1);
s.code = 23;
interception_send(context, device, &stroke, 1);
}else if(s.code == 3){
s.code = 0x1D;
interception_send(context, device, &stroke, 1);
s.code = 44;
interception_send(context, device, &stroke, 1);
if(s.state == 0){
s.code = 0x1D;
interception_send(context, device, &stroke, 1);
s.code = 44;
interception_send(context, device, &stroke, 1);
s.code = 0x1D;
interception_send(context, device, &stroke, 1);
s.code = 44;
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は今、神となったのだ(`・д´・ )