(本記事はv0.3.1時点でのものである)
TL;DR
Joycon-rsを使ってJoycon-rsを検出する
Joycon-rs
の機能を用いて, 現在パソコンに接続しているJoy-Con および 順次パソコンに新規接続してくるJoy-Con を検出することができる.
Joy-Conの管理において機能の主軸となるのはJoyConManager
である.
JoyConManager
はnew()
された際にパソコンに接続されているJoy-Conを内部のテーブルに記録しておく.
そして指定時間ごとにscan()
を実行し, 内部のテーブルを更新する.
またscan()
の際, 新たに見つかったJoy-ConをReceiver
越しに通知してくれる.
つべこべ言うのはここまでにして, まずはサンプルコードを示す.
use joycon_rs::prelude::*;
use std::convert::TryInto;
use std::ops::Deref;
fn main() -> JoyConResult<()> {
// First, connect your Joy-Cons to your computer!
let manager = JoyConManager::new()?;
let (managed_devices, new_devices) = {
let lock = manager.lock();
match lock {
Ok(m) => (m.managed_devices(), m.new_devices()),
Err(_) => unreachable!(),
}
};
managed_devices.into_iter()
.chain(new_devices)
.try_for_each::<_, JoyConResult<()>>(|d| {
if let Ok(device) = d.lock() {
let device: &HidDevice = device.deref().try_into()?;
println!("{:?}", device.get_product_string()?);
}
Ok(())
})?;
Ok(())
}
let manager = JoyConManager::new()?;
でJoyConManager
のインスタンスを作っているのは言うまでもない.
ここで生成されるインスタンスはArc<Mutex<T>>
で包まれているので,
let (managed_devices, new_devices) = {
let lock = manager.lock();
match lock {
Ok(m) => (m.managed_devices(), m.new_devices()),
Err(_) => unreachable!(),
}
};
という形でロックを取得してから, 必要な値であるmanaged_devices
, new_devices
を取り出している.
Arc
やMutex
をご存知でなくても, いまは気にせず先に進んで問題ない.
さて, これで
- 現在パソコンに接続されているJoy-Conのセット:
managed_devices
- 順次パソコンに接続されるJoy-Conの通知チャンネル:
new_devices
が手元に揃ったので, これらに対する処理を実装しよう.
managed_devices.into_iter()
.chain(new_devices)
このサンプルコードの肝といってもいいのがこの部分である. managed_devices
を.into_iter()
して生成されたイテレータに, new_devices
を.chain()
, つまり連結している. そうすることで, 現在把握しているJoy-Conと, これから接続しているJoy-Conに対する操作をまとめて記述できる.
ちなみに, このイテレータはloop{}
ブロックのように振る舞い, 基本的に止まることがない. managed_devices
に束縛されていたJoyConDevice
全部の処理を終えたのちは, new_devices
に束縛されていたReceiver
に新たなJoyConDevice
がやってくるのを口を開けて待っている, そんな状態になる.
もちろん, ループから抜ける場合もある. 例えば, これに続くtry_for_each()
の処理が失敗して, Err
を返さねばならないときなどだ.
下処理が済んだので, 実際の処理を実装しよう.
.try_for_each::<_, JoyConResult<()>>(|d| {
if let Ok(device) = d.lock() {
let device: &HidDevice = device.deref().try_into()?;
println!("{:?}", device.get_product_string()?);
}
Ok(())
})?;
取得したJoy-Conに対してプロダクト名を尋ねている. 返答があればその名前を標準出力に流し, 返答がない, あるいは切断状態であればErr
を返す.
実行
Joy-Con (L)を接続している場合, 次のような表示になる.
$ cargo run --example scan_for_joycons
Compiling joycon-rs v0.3.1 (/hoge/github/joycon-rs)
Finished dev [unoptimized + debuginfo] target(s) in 3.74s
Running `target/debug/examples/scan_for_joycons`
Some("Joy-Con (L)")
ここにJoy-Con (R)を接続すると,
Some("Joy-Con (R)")
という新規の表示が出るはずだ. JoyConManager
のデフォルトのスキャン間隔は1秒なので, 多少は待つかもしれない。JoyConManager::with_interval()
で, スキャン間隔の違うJoyConManager
を生成できる.
備考 / 余談
JoyConManagerのインスタンス数の制限
JoyConManager
はフィールドにhidapi::HidApi
を持っており, その影響でインスタンスを一つしか作れない(2つ目以降を作るとJoyConManager::new()
がErr
を返すはず).
このような場合にはシングルトンパターンを使うのが適切であろうから, 次回ぐらいのアップデートで修正したい. lazy_static
の出番だろうか?
JoyConManager::new_devices()で生成されるReceiverについて
Receiver
はcrossbeam_channel
のmpmc, Multi-Producer Multi-Consumer 型のchannelのものである. いくらでもCloneできるが, メッセージはどれかひとつのReceiver
でrecv()
されれば消える。
また, channelのキャパシティは0に設定されている。生成されているすべてのReceiver
がrecv()
の状態になければ、送信されたメッセージはどこかに消える。
次回
次回は, 側面のLEDを制御する方法について解説する.
-> Next: Joycon-rs の使い方 - 側面LED, HOMEボタンのライト制御
Joycon-rs
v0.3.1の解説記事一覧はこちら.
tags/joycon-rs-v0.3.1