Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added basic mouse capture API #679

Merged
merged 14 commits into from
Oct 16, 2020
6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,11 @@ path = "examples/ui/ui.rs"
name = "clear_color"
path = "examples/window/clear_color.rs"

[[example]]
name = "cursor_lock"
path = "examples/window/cursor_lock.rs"
required-features = ["bevy_winit"]

[[example]]
name = "multiple_windows"
path = "examples/window/multiple_windows.rs"
Expand Down Expand Up @@ -293,3 +298,4 @@ required-features = ["bevy_winit"]
name = "assets_wasm"
path = "examples/wasm/assets_wasm.rs"
required-features = ["bevy_winit"]

26 changes: 26 additions & 0 deletions crates/bevy_window/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,29 @@ pub struct CursorMoved {
pub id: WindowId,
pub position: Vec2,
}

#[derive(Debug, Clone)]
pub enum CursorLockMode {
Locked,
Unlocked,
}

/// Event that should be sent when the cursor should be locked or unlocked.
#[derive(Debug, Clone)]
pub struct ChangeCursorLockState {
pub id: WindowId,
pub mode: CursorLockMode,
}

#[derive(Debug, Clone)]
pub enum CursorShowMode {
Show,
Hide,
}

/// Event that should be sent when the cursor should be hidden or shown.
#[derive(Debug, Clone)]
pub struct ChangeCursorVisibility {
pub id: WindowId,
pub mode: CursorShowMode,
}
2 changes: 2 additions & 0 deletions crates/bevy_window/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ impl Plugin for WindowPlugin {
.add_event::<WindowCloseRequested>()
.add_event::<CloseWindow>()
.add_event::<CursorMoved>()
.add_event::<ChangeCursorLockState>()
.add_event::<ChangeCursorVisibility>()
.init_resource::<Windows>();

if self.add_primary_window {
Expand Down
35 changes: 35 additions & 0 deletions crates/bevy_window/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ pub struct Window {
vsync: bool,
resizable: bool,
decorations: bool,
cursor_visible: bool,
cursor_locked: bool,
mode: WindowMode,
#[cfg(target_arch = "wasm32")]
pub canvas: Option<String>,
Expand Down Expand Up @@ -68,6 +70,12 @@ pub enum WindowCommand {
SetDecorations {
decorations: bool,
},
SetCursorLockMode {
locked: bool,
},
SetCursorVisibility {
visible: bool,
},
}

/// Defines the way a window is displayed
Expand All @@ -92,6 +100,8 @@ impl Window {
vsync: window_descriptor.vsync,
resizable: window_descriptor.resizable,
decorations: window_descriptor.decorations,
cursor_visible: window_descriptor.cursor_visible,
cursor_locked: window_descriptor.cursor_locked,
mode: window_descriptor.mode,
#[cfg(target_arch = "wasm32")]
canvas: window_descriptor.canvas.clone(),
Expand Down Expand Up @@ -165,6 +175,27 @@ impl Window {
.push(WindowCommand::SetDecorations { decorations });
}

pub fn cursor_locked(&self) -> bool {
self.cursor_locked
}

pub fn set_cursor_lock_mode(&mut self, lock_mode: bool) {
self.cursor_locked = lock_mode;
self.command_queue
.push(WindowCommand::SetCursorLockMode { locked: lock_mode });
}

pub fn cursor_visible(&self) -> bool {
self.cursor_visible
}

pub fn set_cursor_visibility(&mut self, visibile_mode: bool) {
self.cursor_visible = visibile_mode;
self.command_queue.push(WindowCommand::SetCursorVisibility {
visible: visibile_mode,
});
}

pub fn mode(&self) -> WindowMode {
self.mode
}
Expand All @@ -191,6 +222,8 @@ pub struct WindowDescriptor {
pub vsync: bool,
pub resizable: bool,
pub decorations: bool,
pub cursor_visible: bool,
pub cursor_locked: bool,
pub mode: WindowMode,
#[cfg(target_arch = "wasm32")]
pub canvas: Option<String>,
Expand All @@ -210,6 +243,8 @@ impl Default for WindowDescriptor {
vsync: true,
resizable: true,
decorations: true,
cursor_locked: false,
cursor_visible: true,
mode: WindowMode::Windowed,
#[cfg(target_arch = "wasm32")]
canvas: None,
Expand Down
52 changes: 51 additions & 1 deletion crates/bevy_winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ use bevy_app::{prelude::*, AppExit};
use bevy_ecs::{IntoThreadLocalSystem, Resources, World};
use bevy_math::Vec2;
use bevy_window::{
CreateWindow, CursorMoved, Window, WindowCloseRequested, WindowCreated, WindowResized, Windows,
ChangeCursorLockState, ChangeCursorVisibility, CreateWindow, CursorLockMode, CursorMoved,
CursorShowMode, Window, WindowCloseRequested, WindowCreated, WindowResized, Windows,
};
use winit::{
event::{self, DeviceEvent, Event, WindowEvent},
Expand Down Expand Up @@ -84,6 +85,14 @@ fn change_window(_: &mut World, resources: &mut Resources) {
let window = winit_windows.get_window(id).unwrap();
window.set_decorations(decorations);
}
bevy_window::WindowCommand::SetCursorLockMode { locked } => {
let window = winit_windows.get_window(id).unwrap();
window.set_cursor_grab(locked).unwrap();
}
bevy_window::WindowCommand::SetCursorVisibility { visible } => {
let window = winit_windows.get_window(id).unwrap();
window.set_cursor_visible(visible);
}
}
}
}
Expand Down Expand Up @@ -136,6 +145,9 @@ pub fn winit_runner(mut app: App) {
let mut event_loop = EventLoop::new();
let mut create_window_event_reader = EventReader::<CreateWindow>::default();
let mut app_exit_event_reader = EventReader::<AppExit>::default();
let mut cursor_lock_state_event_reader = EventReader::<ChangeCursorLockState>::default();
let mut cursor_visibility_change_event_reader =
EventReader::<ChangeCursorVisibility>::default();

app.resources
.insert_thread_local(EventLoopProxyPtr(
Expand Down Expand Up @@ -267,6 +279,11 @@ pub fn winit_runner(mut app: App) {
event_loop,
&mut create_window_event_reader,
);
handle_cursor_state_change_events(
&mut app.resources,
&mut cursor_lock_state_event_reader,
&mut cursor_visibility_change_event_reader,
);
app.update();
}
_ => (),
Expand Down Expand Up @@ -296,3 +313,36 @@ fn handle_create_window_events(
window_created_events.send(WindowCreated { id: window_id });
}
}

fn handle_cursor_state_change_events(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need these events anymore. I'd prefer the Window getters/setters to be the one way to set these things

resources: &mut Resources,
cursor_lock_state_event_reader: &mut EventReader<ChangeCursorLockState>,
cursor_visibility_change_event_reader: &mut EventReader<ChangeCursorVisibility>,
) {
let winit_windows = resources.get_mut::<WinitWindows>().unwrap();
let change_cursor_lock_state_events = resources.get::<Events<ChangeCursorLockState>>().unwrap();
let change_cursor_visibility_events =
resources.get::<Events<ChangeCursorVisibility>>().unwrap();

for cursor_lock_event in cursor_lock_state_event_reader.iter(&change_cursor_lock_state_events) {
let id = cursor_lock_event.id;
let window = winit_windows.get_window(id).unwrap();
window
.set_cursor_grab(match cursor_lock_event.mode {
CursorLockMode::Locked => true,
CursorLockMode::Unlocked => false,
})
.unwrap();
}

for cursor_visibility_event in
cursor_visibility_change_event_reader.iter(&change_cursor_visibility_events)
{
let id = cursor_visibility_event.id;
let window = winit_windows.get_window(id).unwrap();
window.set_cursor_visible(match cursor_visibility_event.mode {
CursorShowMode::Show => true,
CursorShowMode::Hide => false,
});
}
}
5 changes: 5 additions & 0 deletions crates/bevy_winit/src/winit_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ impl WinitWindows {

let winit_window = winit_window_builder.build(&event_loop).unwrap();

winit_window
.set_cursor_grab(window.cursor_locked())
.unwrap();
winit_window.set_cursor_visible(window.cursor_visible());

self.window_id_to_winit
.insert(window.id(), winit_window.id());
self.winit_to_window_id
Expand Down
37 changes: 37 additions & 0 deletions examples/window/cursor_lock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use bevy::{
prelude::*,
window::{
ChangeCursorLockState, ChangeCursorVisibility, CursorLockMode, CursorShowMode, WindowId,
},
};

fn main() {
App::build()
.add_resource(WindowDescriptor {
title: "Press G to grab the cursor".to_string(),
..Default::default()
})
.add_default_plugins()
.add_system(grab_cursor_system.system())
.run()
}

fn grab_cursor_system(
input: Res<Input<KeyCode>>,
mut lock_state: ResMut<Events<ChangeCursorLockState>>,
mut show_state: ResMut<Events<ChangeCursorVisibility>>,
) {
if input.just_pressed(KeyCode::G) {
let id = WindowId::primary();

lock_state.send(ChangeCursorLockState {
id,
mode: CursorLockMode::Locked,
});

show_state.send(ChangeCursorVisibility {
id,
mode: CursorShowMode::Hide,
});
}
}
10 changes: 10 additions & 0 deletions examples/window/window_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ fn main() {
})
.add_default_plugins()
.add_system(change_title.system())
.add_system(toggle_cursor.system())
.run();
}

Expand All @@ -24,3 +25,12 @@ fn change_title(time: Res<Time>, mut windows: ResMut<Windows>) {
time.seconds_since_startup.round()
));
}

/// This system toggles the cursor's visibility when the space bar is pressed
fn toggle_cursor(input: Res<Input<KeyCode>>, mut windows: ResMut<Windows>) {
let window = windows.get_primary_mut().unwrap();
if input.just_pressed(KeyCode::Space) {
window.set_cursor_lock_mode(!window.cursor_locked());
window.set_cursor_visibility(!window.cursor_visible());
}
}