sdl3/keyboard/
mod.rs

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