Files
another-boids-in-rust/vendor/gilrs/examples/gui.rs

330 lines
14 KiB
Rust

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
use crate::egui::plot::{MarkerShape, PlotPoints, Points};
use crate::egui::RichText;
use eframe::egui;
use eframe::egui::Vec2;
use gilrs::ev::AxisOrBtn;
use gilrs::ff::{BaseEffect, BaseEffectType, Effect, EffectBuilder, Repeat, Ticks};
use gilrs::{Axis, GamepadId, Gilrs, GilrsBuilder};
use gilrs_core::PowerInfo;
use std::time::UNIX_EPOCH;
use uuid::Uuid;
struct MyEguiApp {
gilrs: Gilrs,
current_gamepad: Option<GamepadId>,
log_messages: [Option<String>; 300],
// These will be none if Force feedback isn't supported for this platform e.g. Wasm
ff_strong: Option<Effect>,
ff_weak: Option<Effect>,
}
impl Default for MyEguiApp {
fn default() -> Self {
#[cfg(target_arch = "wasm32")]
console_log::init().unwrap();
const INIT: Option<String> = None;
let mut gilrs = GilrsBuilder::new().set_update_state(false).build().unwrap();
let ff_strong = EffectBuilder::new()
.add_effect(BaseEffect {
kind: BaseEffectType::Strong { magnitude: 60_000 },
scheduling: Default::default(),
envelope: Default::default(),
})
.repeat(Repeat::For(Ticks::from_ms(100)))
.finish(&mut gilrs)
.ok();
let ff_weak = EffectBuilder::new()
.add_effect(BaseEffect {
kind: BaseEffectType::Weak { magnitude: 60_000 },
scheduling: Default::default(),
envelope: Default::default(),
})
.repeat(Repeat::For(Ticks::from_ms(100)))
.finish(&mut gilrs)
.ok();
Self {
gilrs,
current_gamepad: None,
log_messages: [INIT; 300],
ff_strong,
ff_weak,
}
}
}
impl MyEguiApp {
fn log(&mut self, message: String) {
self.log_messages[0..].rotate_right(1);
self.log_messages[0] = Some(message);
}
}
impl MyEguiApp {
fn new(_cc: &eframe::CreationContext<'_>) -> Self {
Self::default()
}
}
impl eframe::App for MyEguiApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
while let Some(event) = self.gilrs.next_event() {
self.log(format!(
"{} : {} : {:?}",
event
.time
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis(),
event.id,
event.event
));
self.gilrs.update(&event);
if self.current_gamepad.is_none() {
self.current_gamepad = Some(event.id);
}
}
egui::SidePanel::left("side_panel").show(ctx, |ui| {
ui.heading("Controllers");
ui.separator();
for (id, gamepad) in self.gilrs.gamepads() {
if ui
.selectable_label(
self.current_gamepad == Some(id),
format!("{id}: {}", gamepad.name()),
)
.clicked()
{
self.current_gamepad = Some(id);
};
}
ui.allocate_space(ui.available_size());
});
egui::TopBottomPanel::bottom("log")
.resizable(true)
.default_height(200.0)
.show(ctx, |ui| {
ui.heading("Event Log");
egui::ScrollArea::vertical()
.max_height(ui.available_height())
.show(ui, |ui| {
for message in self.log_messages.iter().flatten() {
ui.label(message);
}
ui.allocate_space(ui.available_size());
});
});
egui::CentralPanel::default().show(ctx, |ui| {
egui::ScrollArea::both().show(ui, |ui| {
if let Some(gamepad_id) = self.current_gamepad {
let gamepad = self.gilrs.gamepad(gamepad_id);
let gamepad_state = gamepad.state();
ui.horizontal(|ui| {
ui.vertical(|ui| {
ui.heading("Info");
egui::Grid::new("info_grid")
.striped(true)
.num_columns(2)
.show(ui, |ui| {
ui.label("Name");
ui.label(gamepad.name());
ui.end_row();
if let Some(vendor) = gamepad.vendor_id() {
ui.label("Vendor ID");
ui.label(format!("{vendor:04x}"));
ui.end_row();
}
if let Some(product) = gamepad.product_id() {
ui.label("Product ID");
ui.label(format!("{product:04x}"));
ui.end_row();
}
ui.label("Gilrs ID");
ui.label(gamepad.id().to_string());
ui.end_row();
if let Some(map_name) = gamepad.map_name() {
ui.label("Map Name");
ui.label(map_name);
ui.end_row();
}
ui.label("Map Source");
ui.label(format!("{:?}", gamepad.mapping_source()));
ui.end_row();
ui.label("Uuid");
let uuid = Uuid::from_bytes(gamepad.uuid()).to_string();
ui.horizontal(|ui| {
ui.label(&uuid);
if ui.button("Copy").clicked() {
ui.output().copied_text = uuid;
}
});
ui.end_row();
ui.label("Power");
ui.label(match gamepad.power_info() {
PowerInfo::Unknown => "Unknown".to_string(),
PowerInfo::Wired => "Wired".to_string(),
PowerInfo::Discharging(p) => format!("Discharging {p}"),
PowerInfo::Charging(p) => format!("Charging {p}"),
PowerInfo::Charged => "Charged".to_string(),
});
ui.end_row();
});
});
if gamepad.is_ff_supported() {
ui.vertical(|ui| {
ui.label("Force Feedback");
if let Some(ff_strong) = &self.ff_strong {
if ui.button("Play Strong").clicked() {
ff_strong.add_gamepad(&gamepad).unwrap();
ff_strong.play().unwrap();
}
}
if let Some(ff_weak) = &self.ff_weak {
if ui.button("Play Weak").clicked() {
ff_weak.add_gamepad(&gamepad).unwrap();
ff_weak.play().unwrap();
}
}
});
}
});
ui.horizontal(|ui| {
ui.vertical(|ui| {
ui.set_width(300.0);
ui.heading("Buttons");
for (code, button_data) in gamepad_state.buttons() {
let name = match gamepad.axis_or_btn_name(code) {
Some(AxisOrBtn::Btn(b)) => format!("{b:?}"),
_ => "Unknown".to_string(),
};
ui.add(
egui::widgets::ProgressBar::new(button_data.value()).text(
RichText::new(format!(
"{name:<14} {:<5} {:.4} {}",
button_data.is_pressed(),
button_data.value(),
code
))
.monospace(),
),
);
}
});
ui.vertical(|ui| {
ui.set_width(300.0);
ui.heading("Axes");
ui.horizontal(|ui| {
for (name, x, y) in [
("Left Stick", Axis::LeftStickX, Axis::LeftStickY),
("Right Stick", Axis::RightStickX, Axis::RightStickY),
] {
ui.vertical(|ui| {
ui.label(name);
let y_axis = gamepad
.axis_data(y)
.map(|a| a.value())
.unwrap_or_default()
as f64;
let x_axis = gamepad
.axis_data(x)
.map(|a| a.value())
.unwrap_or_default()
as f64;
egui::widgets::plot::Plot::new(format!("{name}_plot"))
.width(150.0)
.height(150.0)
.min_size(Vec2::splat(3.25))
.include_x(1.25)
.include_y(1.25)
.include_x(-1.25)
.include_y(-1.25)
.allow_drag(false)
.allow_zoom(false)
.allow_boxed_zoom(false)
.allow_scroll(false)
.show(ui, |plot_ui| {
plot_ui.points(
Points::new(PlotPoints::new(vec![[
x_axis, y_axis,
]]))
.shape(MarkerShape::Circle)
.radius(4.0),
);
});
});
}
});
for (code, axis_data) in gamepad_state.axes() {
let name = match gamepad.axis_or_btn_name(code) {
None => code.to_string(),
Some(AxisOrBtn::Btn(b)) => format!("{b:?}"),
Some(AxisOrBtn::Axis(a)) => format!("{a:?}"),
};
ui.add(
egui::widgets::ProgressBar::new(
(axis_data.value() * 0.5) + 0.5,
)
.text(
RichText::new(format!(
"{:+.4} {name:<15} {}",
axis_data.value(),
code
))
.monospace(),
),
);
}
});
});
} else {
ui.label("Press a button on a controller or select it from the left.");
}
ui.allocate_space(ui.available_size());
});
});
ctx.request_repaint();
}
}
#[cfg(not(target_arch = "wasm32"))]
fn main() {
env_logger::init();
let native_options = eframe::NativeOptions {
initial_window_size: Some(Vec2::new(1024.0, 768.0)),
..Default::default()
};
eframe::run_native(
"Gilrs Input Tester",
native_options,
Box::new(|cc| Box::new(MyEguiApp::new(cc))),
);
}
#[cfg(target_arch = "wasm32")]
fn main() {
console_error_panic_hook::set_once();
let web_options = eframe::WebOptions::default();
eframe::start_web(
"canvas",
web_options,
Box::new(|cc| Box::new(MyEguiApp::new(cc))),
)
.unwrap();
}