Skip to content

Instantly share code, notes, and snippets.

@kb10uy
Created April 28, 2026 09:06
Show Gist options
  • Select an option

  • Save kb10uy/84ce34b8ca4d3eed95d21bf018121dee to your computer and use it in GitHub Desktop.

Select an option

Save kb10uy/84ce34b8ca4d3eed95d21bf018121dee to your computer and use it in GitHub Desktop.
Win11 の InputHapticsManager の機能を試す
#!/usr/bin/env rust-script
//! ```cargo
//! [dependencies]
//! softbuffer = "0.4"
//! winit = "0.30"
//! windows = {
//! git = "https://github.com/microsoft/windows-rs",
//! ref = "1742a73cf7852414e0b304aca148d8b83a586546",
//! features = ["Devices_Haptics"]
//! }
//! ```
use std::num::NonZeroU32;
use std::rc::Rc;
use softbuffer::Surface;
use windows::Devices::Haptics::{InputHapticsManager, KnownSimpleHapticsControllerWaveforms};
use winit::{
application::ApplicationHandler,
dpi::PhysicalSize,
event::WindowEvent,
event_loop::{ActiveEventLoop, EventLoop},
window::{Window, WindowId},
};
const GRID_COLS: u32 = 8;
const GRID_ROWS: u32 = 8;
const COLOR_A: u32 = 0x00_2E_2E_2E; // dark gray
const COLOR_B: u32 = 0x00_4A_4A_4A; // lighter gray
fn main() {
let event_loop = EventLoop::new().expect("failed to initialize EventLoop");
let mut app = App::new();
event_loop.run_app(&mut app).expect("failed to start");
}
struct App {
window: Option<Rc<Window>>,
surface: Option<Surface<Rc<Window>, Rc<Window>>>,
current_cell: Option<(u32, u32)>,
}
impl App {
fn new() -> Self {
Self {
window: None,
surface: None,
current_cell: None,
}
}
fn draw_grid(&mut self) {
let (Some(window), Some(surface)) = (&self.window, &mut self.surface) else {
return;
};
let PhysicalSize { width, height } = window.inner_size();
if width * height == 0 {
return;
}
surface
.resize(
NonZeroU32::new(width).unwrap(),
NonZeroU32::new(height).unwrap(),
)
.ok();
let Ok(mut buffer) = surface.buffer_mut() else {
return;
};
for y in 0..height {
for x in 0..width {
let col = (x * GRID_COLS / width).min(GRID_COLS - 1);
let row = (y * GRID_ROWS / height).min(GRID_ROWS - 1);
let checkerboard = (col + row).is_multiple_of(2);
buffer[(y * width + x) as usize] = if checkerboard { COLOR_A } else { COLOR_B };
}
}
buffer.present().ok();
}
fn send_haptic(&self) {
let Ok(manager) = InputHapticsManager::GetForCurrentThread() else {
return;
};
manager
.TrySendHapticWaveform(
KnownSimpleHapticsControllerWaveforms::Hover().expect("should have value"),
0,
)
.ok();
}
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
if self.window.is_none() {
let attrs = Window::default_attributes()
.with_title("InputHapticsManager test (sends when cursor crosses the grid");
match event_loop.create_window(attrs) {
Ok(window) => {
let window = Rc::new(window);
let context = softbuffer::Context::new(window.clone()).unwrap();
let surface = Surface::new(&context, window.clone()).unwrap();
self.window = Some(window);
self.surface = Some(surface);
}
Err(e) => {
eprintln!("Failed to create window: {e}");
event_loop.exit();
}
}
}
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
match event {
WindowEvent::CloseRequested => {
event_loop.exit();
}
WindowEvent::RedrawRequested => {
self.draw_grid();
}
WindowEvent::Resized(_) => {
if let Some(window) = &self.window {
window.request_redraw();
}
}
WindowEvent::CursorMoved { position, .. } => {
if let Some(window) = &self.window {
let size = window.inner_size();
let col = ((position.x / size.width as f64) * GRID_COLS as f64) as u32;
let row = ((position.y / size.height as f64) * GRID_ROWS as f64) as u32;
let col = col.min(GRID_COLS - 1);
let row = row.min(GRID_ROWS - 1);
let new_cell = (col, row);
if self.current_cell != Some(new_cell) {
if self.current_cell.is_some() {
self.send_haptic();
}
self.current_cell = Some(new_cell);
}
}
}
_ => {}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment