LoginSignup
7
5

More than 5 years have passed since last update.

VIVEトラッカーのボタンを活用する話

Last updated at Posted at 2019-04-23

はじめに

VRChatをプレイ中、GeForce Experienceで録画するときに毎回キーボードを探して入力するのが面倒だったのですが、トラッカーのボタン使えたら便利じゃね?と思ったのが今回のきっかけです。

※今回の話はVIVEトラッカー(2018)でしか動作確認していないので旧VIVEトラッカーでは動かない可能性があります。

VIVEトラッカー

VRChatでフルボディトラッキングをするために使うVIVEトラッカーですが、電源ボタンとPOGO pinを利用して5つまでのボタン入力を扱うことができます。
しかし、現在のSteamVRのバージョンではコントローラの入力を左右の手で行うことしか想定されていないため、3台目以降のコントローラ及びトラッカーのボタン入力を取得することができません。
ではどうするのかというと、トラッカーのHID Inputレポートからボタン入力を取得します。

実装

HIDの実装にはhidapiを利用します。また、VIVEコントローラで似たようなことをしている人がいたので参考にしました。

まず、トラッカー(正確にはトラッカーとペアリングされたドングル)のInputレポートを読み取るためには、トラッカーのベンダーIDとプロダクトIDとシリアル番号を知る必要があります。トラッカーのHID情報はhidpaiのhid_enumerateという関数を利用して知ることができます。

#include <stdio.h>
#include "hidapi.h"

int main(int argc, char* argv[]){
  //Enumerate and print the HID devices on the system
  struct hid_device_info *devs, *cur_dev;

  devs = hid_enumerate(0x0, 0x0);
  cur_dev = devs;
  while (1) {
    fprintf(stderr, "Device Found\n  type: %04hx %04hx\n  path: %s\n  serial_number: %ls",
      cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
    fprintf(stderr, "\n");
    fprintf(stderr, "  Manufacturer: %ls\n", cur_dev->manufacturer_string);
    fprintf(stderr, "  Product:      %ls\n", cur_dev->product_string);
    fprintf(stderr, "\n");
    if (cur_dev->next) {
      cur_dev = cur_dev->next;
    }
    else {
      break;
    }
  }
  hid_free_enumeration(devs);
  return 0;
}

これを実行すると、トラッカのベンダーIDは0x28deで、プロダクトIDは0x2101であり、トラッカーごとに別々のシリアル番号があるということが分かりますが、VIVEコントローラのベンダーIDとプロダクトIDも同じ値なので、注意が必要です。

トラッカーのInputレポートには様々な情報が含まれますが、ボタン入力を取得するためにはInputレポートの4バイト目と6バイト目のデータを見る必要があります。

4バイト目の値 6バイト目の値 ボタン
0x20 0x01 Trigger
0x20 0x04 Pad
0x20 0x08 Power
0x20 0x10 Grip
0x20 0x20 Menu

ボタン入力があったとき4バイト目の値が0x20になり、6バイト目のデータからどのボタンが押されたかが分かります。また、ボタンを離したときも、4バイト目の値が0x20になり、6バイト目の押していたボタンに対応するビット値が0になります。

以上のことを踏まえて、トラッカーのボタンでGeForce Experienceを利用した録画するためのコードが以下のようになります。

#include <stdio.h>
#include <windows.h>
#include "hidapi.h"

#define VID 0x28de
#define PID 0x2101

wchar_t serial_number[] = L"3A6F5B0637"

int main(int argc, char* argv[]){
  int res;
  unsigned char buf[128] = { 0 };
  hid_device *handle;

  // Open the device
  handle = hid_open(VID, PID, serial_number);

  if (!handle) {
    fprintf(stderr, "Couldn't get hid handle\n");
    exit(1);
  }

  while (1) {
    res = hid_read(handle, buf, 128);
    if (!res) continue;
    if (buf[4] == 0x20) {
      if ((buf[6] & 0x08) == 0x08){
        fprintf(stderr, "power\n");
        MessageBeep(MB_ICONINFORMATION);
        keybd_event(VK_LMENU, 0x00, KEYEVENTF_EXTENDEDKEY | 0, 0);  //ALT
        keybd_event(VK_F9, 0x00, KEYEVENTF_EXTENDEDKEY | 0, 0); //F9
        keybd_event(VK_F9, 0x00, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
        keybd_event(VK_LMENU, 0x00, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
        Sleep(100); //Chattering prevention
        }
    }
  }
  return 0;
}

GeForce Experienceで録画するためにはキーボードのALTとF9を同時に入力する必要があるのでkeybd_eventという関数を使っています。また、HMDをかぶっていると録画が開始されたかわからないので、ボタンが押されたときに音を出すようにしています。ボタンのチャタリングの影響で同じInputレポートを何回も送信することがあるので、100msの待ち時間を追加しています。

おわりに

トラッカーのHID Imputレポートを読み取ることで、簡単にボタン入力を取得することができました、プログラムを工夫することで、ボタンの長押しやダブルクリックにも対応できそうです。今回の目的以外にも結構応用できそうなので、是非試してみてください。

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5