browser_input 0.2.0

Ergonomic input handling for WebAssembly applications in the browser with keyboard, mouse, and event processing
docs.rs failed to build browser_input-0.2.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

🎮 browser_input

Ergonomic input handling for WebAssembly applications in the browser

A lightweight, efficient input handling library specifically designed for Rust WebAssembly applications. Provides a unified interface for capturing and processing keyboard, mouse, and other input events in web browsers.

✨ Features

🎯 Input Types

  • Keyboard Input - Full keyboard event capture with modifier support
  • Mouse Buttons - Left, right, middle mouse button handling
  • Mouse Movement - Precise pointer position and movement tracking
  • Mouse Wheel - Scroll events (planned)

🛠️ Core Capabilities

  • Event Queue System - Buffered input events with modifier key states
  • State Tracking - Real-time key and button state queries
  • Modifier Support - Alt, Ctrl, Shift key combination detection
  • Browser Integration - Direct DOM event binding with WebAssembly
  • Memory Efficient - Minimal overhead for web deployment

🚀 Quick Start

Add to Your Project

[dependencies]
browser_input = { workspace = true, features = ["enabled"] }

Basic Input Loop

use browser_input::*;

let mut input = Input::new( None, CLIENT );

loop
{
  // Update input state from DOM events
  input.update_state();

  // Process event queue
  for Event { event_type, alt, ctrl, shift } in input.event_queue().as_slice()
  {
    match event_type
    {
      EventType::MouseButton( button, Action::Press ) =>
      {
        println!( "Mouse button {:?} pressed with modifiers: Alt={}, Ctrl={}, Shift={}", 
                 button, alt, ctrl, shift );
      }
      EventType::KeyBoard( key, Action::Press ) =>
      {
        println!( "Key {:?} pressed", key );
      }
      _ => {}
    }
  }

  // Direct state queries
  let pointer_pos = input.pointer_position();
  let is_w_down = input.is_key_down( keyboard::KeyboardKey::KeyW );
  let is_space_down = input.is_key_down( keyboard::KeyboardKey::Space );

  if is_w_down && ctrl {
    // Handle Ctrl+W combination
  }

  // CRITICAL: Clear events at end of frame
  input.clear_events();
}

Game Input Example

use browser_input::*;

struct GameInput
{
  input: Input,
  player_speed: f32,
}

impl GameInput
{
  pub fn new() -> Self
  {
    Self
    {
      input: Input::new( None, CLIENT ),
      player_speed: 5.0,
    }
  }

  pub fn update( &mut self ) -> PlayerMovement
  {
    self.input.update_state();
    
    let mut movement = PlayerMovement::default();
    
    // WASD movement
    if self.input.is_key_down( keyboard::KeyboardKey::KeyW ) {
      movement.forward = self.player_speed;
    }
    if self.input.is_key_down( keyboard::KeyboardKey::KeyS ) {
      movement.backward = self.player_speed;
    }
    if self.input.is_key_down( keyboard::KeyboardKey::KeyA ) {
      movement.left = self.player_speed;
    }
    if self.input.is_key_down( keyboard::KeyboardKey::KeyD ) {
      movement.right = self.player_speed;
    }
    
    // Mouse look
    movement.mouse_pos = self.input.pointer_position();
    
    // Process discrete events
    for event in self.input.event_queue().as_slice()
    {
      if let EventType::MouseButton( MouseButton::Left, Action::Press ) = event.event_type
      {
        movement.shoot = true;
      }
    }
    
    self.input.clear_events();
    movement
  }
}

#[ derive( Default ) ]
struct PlayerMovement
{
  forward: f32,
  backward: f32,
  left: f32,
  right: f32,
  mouse_pos: ( f32, f32 ),
  shoot: bool,
}

📚 API Reference

Core Types

Input

Main input manager struct:

impl Input
{
  // Create new input handler
  pub fn new( canvas: Option< web_sys::HtmlCanvasElement >, client: ClientType ) -> Self;
  
  // Update state from DOM events (call once per frame)
  pub fn update_state( &mut self );
  
  // Get current event queue
  pub fn event_queue( &self ) -> &Vec< Event >;
  
  // Clear event queue (call at end of frame)
  pub fn clear_events( &mut self );
  
  // Query key state
  pub fn is_key_down( &self, key: keyboard::KeyboardKey ) -> bool;
  
  // Get current pointer position
  pub fn pointer_position( &self ) -> ( f32, f32 );
}

Event

Input event with modifier keys:

pub struct Event
{
  pub event_type: EventType,
  pub alt: bool,
  pub ctrl: bool,
  pub shift: bool,
}

EventType

Different types of input events:

pub enum EventType
{
  MouseButton( MouseButton, Action ),
  KeyBoard( keyboard::KeyboardKey, Action ),
  MouseMove( f32, f32 ), // x, y coordinates
}

Key Constants

Keyboard Keys

use browser_input::keyboard::KeyboardKey;

// Movement keys
KeyboardKey::KeyW
KeyboardKey::KeyA
KeyboardKey::KeyS
KeyboardKey::KeyD

// Arrow keys
KeyboardKey::ArrowUp
KeyboardKey::ArrowDown
KeyboardKey::ArrowLeft
KeyboardKey::ArrowRight

// Action keys
KeyboardKey::Space
KeyboardKey::Enter
KeyboardKey::Escape
KeyboardKey::Tab

// Number keys
KeyboardKey::Digit1
KeyboardKey::Digit2
// ... etc

Mouse Buttons

use browser_input::MouseButton;

MouseButton::Left
MouseButton::Right
MouseButton::Middle

🎯 Usage Patterns

Real-Time Controls

For games and interactive applications requiring immediate response:

// Query current state directly
let move_up = input.is_key_down( KeyboardKey::KeyW );
let move_down = input.is_key_down( KeyboardKey::KeyS );
let shooting = input.is_key_down( KeyboardKey::Space );

Event-Driven Actions

For UI interactions and discrete actions:

for event in input.event_queue().as_slice()
{
  match event.event_type
  {
    EventType::KeyBoard( KeyboardKey::Enter, Action::Press ) => {
      // Handle menu selection
    }
    EventType::MouseButton( MouseButton::Left, Action::Release ) => {
      // Handle button click
    }
    _ => {}
  }
}

Modifier Key Combinations

for Event { event_type, ctrl, alt, shift } in input.event_queue().as_slice()
{
  if let EventType::KeyBoard( KeyboardKey::KeyS, Action::Press ) = event_type
  {
    if *ctrl {
      save_file(); // Ctrl+S
    } else if *alt {
      save_as(); // Alt+S
    } else {
      // Regular 'S' press
    }
  }
}

⚠️ Important Notes

Frame Management

  • Always call update_state() at the beginning of your main loop
  • Always call clear_events() at the end of your main loop
  • Failure to clear events will cause the event queue to grow indefinitely

Canvas Binding

// Bind to specific canvas
let canvas = document.get_element_by_id( "game-canvas" )
  .unwrap()
  .dyn_into::< web_sys::HtmlCanvasElement >()
  .unwrap();
let input = Input::new( Some( canvas ), CLIENT );

// Bind to entire document (default)
let input = Input::new( None, CLIENT );

Performance

  • Event processing is O(n) where n is the number of events per frame
  • State queries (is_key_down) are O(1) constant time
  • Memory usage scales with the number of simultaneous key presses

🤝 Contributing

This crate is part of the CGTools workspace. Feel free to submit issues and pull requests on GitHub.

📄 License

MIT