sdl2/keyboard/
mod.rs

1// 0 should not be used in bitflags, but here it is. Removing it will break existing code.
2#![allow(clippy::bad_bit_mask)]
3
4use crate::rect::Rect;
5use crate::video::Window;
6use crate::EventPump;
7
8use std::fmt;
9use std::iter::FilterMap;
10use std::mem::transmute;
11
12use crate::sys;
13
14mod keycode;
15mod scancode;
16pub use self::keycode::Keycode;
17pub use self::scancode::Scancode;
18
19bitflags! {
20    pub struct Mod: u16 {
21        const NOMOD = 0x0000;
22        const LSHIFTMOD = 0x0001;
23        const RSHIFTMOD = 0x0002;
24        const LCTRLMOD = 0x0040;
25        const RCTRLMOD = 0x0080;
26        const LALTMOD = 0x0100;
27        const RALTMOD = 0x0200;
28        const LGUIMOD = 0x0400;
29        const RGUIMOD = 0x0800;
30        const NUMMOD = 0x1000;
31        const CAPSMOD = 0x2000;
32        const MODEMOD = 0x4000;
33        const RESERVEDMOD = 0x8000;
34    }
35}
36
37impl fmt::Display for Mod {
38    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39        write!(f, "{:04x}", *self)
40    }
41}
42
43pub struct KeyboardState<'a> {
44    keyboard_state: &'a [u8],
45}
46
47impl<'a> KeyboardState<'a> {
48    #[doc(alias = "SDL_GetKeyboardState")]
49    pub fn new(_e: &'a EventPump) -> KeyboardState<'a> {
50        let keyboard_state = unsafe {
51            let mut count = 0;
52            let state_ptr = sys::SDL_GetKeyboardState(&mut count);
53
54            ::std::slice::from_raw_parts(state_ptr, count as usize)
55        };
56
57        KeyboardState { keyboard_state }
58    }
59
60    /// Returns true if the scancode is pressed.
61    ///
62    /// # Example
63    /// ```no_run
64    /// use sdl2::keyboard::Scancode;
65    ///
66    /// fn is_a_pressed(e: &sdl2::EventPump) -> bool {
67    ///     e.keyboard_state().is_scancode_pressed(Scancode::A)
68    /// }
69    /// ```
70    pub fn is_scancode_pressed(&self, scancode: Scancode) -> bool {
71        self.keyboard_state[scancode as i32 as usize] != 0
72    }
73
74    /// Returns an iterator all scancodes with a boolean indicating if the scancode is pressed.
75    pub fn scancodes(&self) -> ScancodeIterator {
76        ScancodeIterator {
77            index: 0,
78            keyboard_state: self.keyboard_state,
79        }
80    }
81
82    /// Returns an iterator of pressed scancodes.
83    ///
84    /// # Example
85    /// ```no_run
86    /// use sdl2::keyboard::Keycode;
87    /// use sdl2::keyboard::Scancode;
88    /// use std::collections::HashSet;
89    ///
90    /// fn pressed_scancode_set(e: &sdl2::EventPump) -> HashSet<Scancode> {
91    ///     e.keyboard_state().pressed_scancodes().collect()
92    /// }
93    ///
94    /// fn pressed_keycode_set(e: &sdl2::EventPump) -> HashSet<Keycode> {
95    ///     e.keyboard_state().pressed_scancodes()
96    ///         .filter_map(Keycode::from_scancode)
97    ///         .collect()
98    /// }
99    ///
100    /// fn newly_pressed(old: &HashSet<Scancode>, new: &HashSet<Scancode>) -> HashSet<Scancode> {
101    ///     new - old
102    ///     // sugar for: new.difference(old).collect()
103    /// }
104    /// ```
105    pub fn pressed_scancodes(&self) -> PressedScancodeIterator {
106        self.scancodes().into_pressed_scancode_iter()
107    }
108}
109
110pub struct ScancodeIterator<'a> {
111    index: i32,
112    keyboard_state: &'a [u8],
113}
114
115impl<'a> Iterator for ScancodeIterator<'a> {
116    type Item = (Scancode, bool);
117
118    fn next(&mut self) -> Option<(Scancode, bool)> {
119        if self.index < self.keyboard_state.len() as i32 {
120            let index = self.index;
121            self.index += 1;
122
123            if let Some(scancode) = Scancode::from_i32(index) {
124                let pressed = self.keyboard_state[index as usize] != 0;
125
126                Some((scancode, pressed))
127            } else {
128                self.next()
129            }
130        } else {
131            None
132        }
133    }
134}
135
136impl<'a> ScancodeIterator<'a> {
137    fn into_pressed_scancode_iter(self) -> PressedScancodeIterator<'a> {
138        self.filter_map(|(scancode, pressed)| pressed.then_some(scancode))
139    }
140}
141
142pub type PressedScancodeIterator<'a> =
143    FilterMap<ScancodeIterator<'a>, fn((Scancode, bool)) -> Option<Scancode>>;
144
145impl crate::Sdl {
146    #[inline]
147    pub fn keyboard(&self) -> KeyboardUtil {
148        KeyboardUtil {
149            _sdldrop: self.sdldrop(),
150        }
151    }
152}
153
154impl crate::VideoSubsystem {
155    #[inline]
156    pub fn text_input(&self) -> TextInputUtil {
157        TextInputUtil {
158            _subsystem: self.clone(),
159        }
160    }
161}
162
163/// Keyboard utility functions. Access with `Sdl::keyboard()`.
164///
165/// ```no_run
166/// let sdl_context = sdl2::init().unwrap();
167///
168/// let focused = sdl_context.keyboard().focused_window_id().is_some();
169/// ```
170pub struct KeyboardUtil {
171    _sdldrop: crate::SdlDrop,
172}
173
174impl KeyboardUtil {
175    /// Gets the id of the window which currently has keyboard focus.
176    #[doc(alias = "SDL_GetKeyboardFocus")]
177    pub fn focused_window_id(&self) -> Option<u32> {
178        let raw = unsafe { sys::SDL_GetKeyboardFocus() };
179        if raw.is_null() {
180            None
181        } else {
182            let id = unsafe { sys::SDL_GetWindowID(raw) };
183            Some(id)
184        }
185    }
186
187    #[doc(alias = "SDL_GetModState")]
188    pub fn mod_state(&self) -> Mod {
189        unsafe { Mod::from_bits(sys::SDL_GetModState() as u16).unwrap() }
190    }
191
192    #[doc(alias = "SDL_SetModState")]
193    pub fn set_mod_state(&self, flags: Mod) {
194        unsafe {
195            sys::SDL_SetModState(transmute::<u32, sys::SDL_Keymod>(flags.bits() as u32));
196        }
197    }
198}
199
200/// Text input utility functions. Access with `VideoSubsystem::text_input()`.
201///
202/// These functions require the video subsystem to be initialized and are not thread-safe.
203///
204/// ```no_run
205/// let sdl_context = sdl2::init().unwrap();
206/// let video_subsystem = sdl_context.video().unwrap();
207///
208/// // Start accepting text input events...
209/// video_subsystem.text_input().start();
210/// ```
211pub struct TextInputUtil {
212    _subsystem: crate::VideoSubsystem,
213}
214
215impl TextInputUtil {
216    #[doc(alias = "SDL_StartTextInput")]
217    pub fn start(&self) {
218        unsafe {
219            sys::SDL_StartTextInput();
220        }
221    }
222
223    #[doc(alias = "SDL_IsTextInputActive")]
224    pub fn is_active(&self) -> bool {
225        unsafe { sys::SDL_IsTextInputActive() == sys::SDL_bool::SDL_TRUE }
226    }
227
228    #[doc(alias = "SDL_StopTextInput")]
229    pub fn stop(&self) {
230        unsafe {
231            sys::SDL_StopTextInput();
232        }
233    }
234
235    #[doc(alias = "SDL_SetTextInputRect")]
236    pub fn set_rect(&self, rect: Rect) {
237        unsafe {
238            sys::SDL_SetTextInputRect(rect.raw() as *mut sys::SDL_Rect);
239        }
240    }
241
242    #[doc(alias = "SDL_HasScreenKeyboardSupport")]
243    pub fn has_screen_keyboard_support(&self) -> bool {
244        unsafe { sys::SDL_HasScreenKeyboardSupport() == sys::SDL_bool::SDL_TRUE }
245    }
246
247    #[doc(alias = "SDL_IsScreenKeyboardShown")]
248    pub fn is_screen_keyboard_shown(&self, window: &Window) -> bool {
249        unsafe { sys::SDL_IsScreenKeyboardShown(window.raw()) == sys::SDL_bool::SDL_TRUE }
250    }
251}