Skip to main content

macroquad_ply/
input.rs

1//! Cross-platform mouse, keyboard (and gamepads soon) module.
2
3use std::collections::HashSet;
4
5use crate::prelude::screen_height;
6use crate::prelude::screen_width;
7use crate::Vec2;
8use crate::{get_context, DroppedFile};
9pub use miniquad::{KeyCode, MouseButton};
10
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub enum TouchPhase {
13    Started,
14    Stationary,
15    Moved,
16    Ended,
17    Cancelled,
18}
19
20impl From<miniquad::TouchPhase> for TouchPhase {
21    fn from(miniquad_phase: miniquad::TouchPhase) -> TouchPhase {
22        match miniquad_phase {
23            miniquad::TouchPhase::Started => TouchPhase::Started,
24            miniquad::TouchPhase::Moved => TouchPhase::Moved,
25            miniquad::TouchPhase::Ended => TouchPhase::Ended,
26            miniquad::TouchPhase::Cancelled => TouchPhase::Cancelled,
27        }
28    }
29}
30
31#[derive(Clone, Debug)]
32pub struct Touch {
33    pub id: u64,
34    pub phase: TouchPhase,
35    pub position: Vec2,
36}
37
38/// Constrain mouse to window
39pub fn set_cursor_grab(grab: bool) {
40    let context = get_context();
41    context.cursor_grabbed = grab;
42    miniquad::window::set_cursor_grab(grab);
43}
44
45/// Set mouse cursor visibility
46pub fn show_mouse(shown: bool) {
47    miniquad::window::show_mouse(shown);
48}
49
50/// Return mouse position in pixels.
51pub fn mouse_position() -> (f32, f32) {
52    let context = get_context();
53
54    (
55        context.mouse_position.x / miniquad::window::dpi_scale(),
56        context.mouse_position.y / miniquad::window::dpi_scale(),
57    )
58}
59
60/// Return mouse position in range [-1; 1].
61pub fn mouse_position_local() -> Vec2 {
62    let (pixels_x, pixels_y) = mouse_position();
63
64    convert_to_local(Vec2::new(pixels_x, pixels_y))
65}
66
67/// Returns the difference between the current mouse position and the mouse position on the previous frame.
68pub fn mouse_delta_position() -> Vec2 {
69    let context = get_context();
70
71    let current_position = mouse_position_local();
72    let last_position = context.last_mouse_position.unwrap_or(current_position);
73
74    // Calculate the delta
75    last_position - current_position
76}
77
78/// This is set to true by default, meaning touches will raise mouse events in addition to raising touch events.
79/// If set to false, touches won't affect mouse events.
80pub fn is_simulating_mouse_with_touch() -> bool {
81    get_context().simulate_mouse_with_touch
82}
83
84/// This is set to true by default, meaning touches will raise mouse events in addition to raising touch events.
85/// If set to false, touches won't affect mouse events.
86pub fn simulate_mouse_with_touch(option: bool) {
87    get_context().simulate_mouse_with_touch = option;
88}
89
90/// Return touches with positions in pixels.
91pub fn touches() -> Vec<Touch> {
92    get_context().touches.values().cloned().collect()
93}
94
95/// Return touches with positions in range [-1; 1].
96pub fn touches_local() -> Vec<Touch> {
97    get_context()
98        .touches
99        .values()
100        .map(|touch| {
101            let mut touch = touch.clone();
102            touch.position = convert_to_local(touch.position);
103            touch
104        })
105        .collect()
106}
107
108pub fn mouse_wheel() -> (f32, f32) {
109    let context = get_context();
110
111    (context.mouse_wheel.x, context.mouse_wheel.y)
112}
113
114/// Detect if the key has been pressed once
115pub fn is_key_pressed(key_code: KeyCode) -> bool {
116    let context = get_context();
117
118    context.keys_pressed.contains(&key_code)
119}
120
121/// Detect if the key is being pressed
122pub fn is_key_down(key_code: KeyCode) -> bool {
123    let context = get_context();
124
125    context.keys_down.contains(&key_code)
126}
127
128/// Detect if the key has been released this frame
129pub fn is_key_released(key_code: KeyCode) -> bool {
130    let context = get_context();
131
132    context.keys_released.contains(&key_code)
133}
134
135/// Detect if any key is being preseed
136pub fn is_any_key_down() -> bool {
137    let context = get_context();
138    context.keys_down.len() > 0
139}
140
141/// Return the last pressed char.
142/// Each "get_char_pressed" call will consume a character from the input queue.
143pub fn get_char_pressed() -> Option<char> {
144    let context = get_context();
145
146    context.chars_pressed_queue.pop()
147}
148
149pub(crate) fn get_char_pressed_ui() -> Option<char> {
150    let context = get_context();
151
152    context.chars_pressed_ui_queue.pop()
153}
154
155/// Return the last pressed key.
156pub fn get_last_key_pressed() -> Option<KeyCode> {
157    let context = get_context();
158    // TODO: this will return a random key from keys_pressed HashMap instead of the last one, fix me later
159    context.keys_pressed.iter().next().cloned()
160}
161
162pub fn get_keys_pressed() -> HashSet<KeyCode> {
163    let context = get_context();
164    context.keys_pressed.clone()
165}
166
167pub fn get_keys_down() -> HashSet<KeyCode> {
168    let context = get_context();
169    context.keys_down.clone()
170}
171
172pub fn get_keys_released() -> HashSet<KeyCode> {
173    let context = get_context();
174    context.keys_released.clone()
175}
176
177/// Clears input queue
178pub fn clear_input_queue() {
179    let context = get_context();
180    context.chars_pressed_queue.clear();
181    context.chars_pressed_ui_queue.clear();
182}
183
184/// Detect if the button is being pressed
185pub fn is_mouse_button_down(btn: MouseButton) -> bool {
186    let context = get_context();
187
188    context.mouse_down.contains(&btn)
189}
190
191/// Detect if the button has been pressed once
192pub fn is_mouse_button_pressed(btn: MouseButton) -> bool {
193    let context = get_context();
194
195    context.mouse_pressed.contains(&btn)
196}
197
198/// Detect if the button has been released this frame
199pub fn is_mouse_button_released(btn: MouseButton) -> bool {
200    let context = get_context();
201
202    context.mouse_released.contains(&btn)
203}
204
205/// Convert a position in pixels to a position in the range [-1; 1].
206fn convert_to_local(pixel_pos: Vec2) -> Vec2 {
207    Vec2::new(pixel_pos.x / screen_width(), pixel_pos.y / screen_height()) * 2.0
208        - Vec2::new(1.0, 1.0)
209}
210
211/// Prevents quit
212pub fn prevent_quit() {
213    get_context().prevent_quit_event = true;
214}
215
216/// Detect if quit has been requested
217pub fn is_quit_requested() -> bool {
218    get_context().quit_requested
219}
220
221/// Gets the files which have been dropped on the window.
222pub fn get_dropped_files() -> Vec<DroppedFile> {
223    get_context().dropped_files()
224}
225
226/// Functions for advanced input processing.
227///
228/// Functions in this module should be used by external tools that uses miniquad system, like different UI libraries. User shouldn't use this function.
229pub mod utils {
230    use crate::get_context;
231
232    /// Register input subscriber. Returns subscriber identifier that must be used in `repeat_all_miniquad_input`.
233    pub fn register_input_subscriber() -> usize {
234        let context = get_context();
235
236        context.input_events.push(vec![]);
237
238        context.input_events.len() - 1
239    }
240
241    /// Repeats all events that came since last call of this function with current value of `subscriber`. This function must be called at each frame.
242    pub fn repeat_all_miniquad_input<T: miniquad::EventHandler>(t: &mut T, subscriber: usize) {
243        let context = get_context();
244
245        for event in &context.input_events[subscriber] {
246            event.repeat(t);
247        }
248        context.input_events[subscriber].clear();
249    }
250}