diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e4814a..da5d03b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,13 @@ durch direktes RGB565-Truecolor. Beide Varianten bleiben unabhängig nutzbar: - `pbio::rgb565(r: u8, g: u8, b: u8) -> u16` (const fn) — packt 8-Bit-Kanäle in einen RGB565-Pixelwert. +- `Key::CapsLock` als Tasten-Event (X11, Wayland, Windows — winit 0.30 meldet + Capslock auf macOS nicht als `KeyboardInput`). +- `Platform::caps_lock() -> Option` — Capslock-Zustand, heuristisch + abgeleitet, da winit ihn nicht plattformübergreifend liefert: `CapsLock`-Druck + toggelt, ein getippter Buchstabe (A–Z-Taste ohne Modifier) setzt ihn anhand + von Groß-/Kleinschreibung. `None`, bis der erste Buchstabe getippt wurde. + ### Entfernt - `PlatformConfig::palette` sowie `palette()` / `palette_mut()` und das diff --git a/examples/starter.rs b/examples/starter.rs index 0f5a986..b3b3b43 100644 --- a/examples/starter.rs +++ b/examples/starter.rs @@ -35,6 +35,7 @@ fn main() { while let Some(ev) = p.poll_event() { match ev { Event::Key { key: Key::Escape, pressed: true, .. } => p.request_close(), + Event::Key { key: Key::CapsLock, pressed: true, .. } => eprintln!("CapsLock, Zustand: {:?}", p.caps_lock()), Event::MouseMove { x, y } => mouse = (x, y), Event::MouseBtn { button: MouseButton::Left, pressed } => paint = pressed, _ => {} diff --git a/src/lib.rs b/src/lib.rs index ae7330e..afdc90d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ use winit::event::{ DeviceEvent, DeviceId, ElementState, KeyEvent, MouseButton as WinitMouseButton, WindowEvent, }; use winit::event_loop::{ActiveEventLoop, EventLoop}; -use winit::keyboard::{KeyCode, PhysicalKey}; +use winit::keyboard::{KeyCode, ModifiersState, PhysicalKey}; use winit::platform::pump_events::EventLoopExtPumpEvents; use winit::window::{CursorGrabMode, Window, WindowAttributes, WindowId}; @@ -56,7 +56,7 @@ pub enum Key { Num0, Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Up, Down, Left, Right, - Space, Enter, Escape, Tab, Backspace, + Space, Enter, Escape, Tab, Backspace, CapsLock, LShift, RShift, LCtrl, RCtrl, LAlt, RAlt, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, } @@ -117,6 +117,8 @@ pub struct Platform { // Mode-Wechsel/erstem Frame, damit ein Cursor-Sprung keinen Riesen-Delta erzeugt. last_cursor_pos: Option<(f64, f64)>, mouse_delta: (f32, f32), + modifiers: ModifiersState, + caps_lock: Option, } impl Platform { @@ -233,6 +235,8 @@ impl Platform { mouse_capture: cfg.mouse_capture, last_cursor_pos: None, mouse_delta: (0.0, 0.0), + modifiers: ModifiersState::empty(), + caps_lock: None, }; platform.apply_cursor_mode(); platform @@ -272,6 +276,13 @@ impl Platform { std::mem::replace(&mut self.mouse_delta, (0.0, 0.0)) } + /// Capslock-Zustand, soweit bekannt. winit liefert ihn nicht direkt, daher + /// abgeleitet: `CapsLock`-Druck toggelt, getippte Buchstaben ohne Modifier + /// korrigieren (Groß → an, klein → aus). `None` bis zum ersten Buchstaben. + /// Auf macOS meldet winit 0.30 keine `CapsLock`-Key-Events — dort zählen + /// nur die Buchstaben. + pub fn caps_lock(&self) -> Option { self.caps_lock } + pub fn should_close(&self) -> bool { self.close_requested } pub fn request_close(&mut self) { self.close_requested = true; } @@ -279,13 +290,13 @@ impl Platform { // Felder destructuren, um disjoint &mut-Borrows in den Handler zu reichen. let Self { event_loop, event_queue, close_requested, last_cursor_pos, mouse_delta, - surface, device, config, params, params_buf, queue, content_rect, + modifiers, caps_lock, surface, device, config, params, params_buf, queue, content_rect, aspect_ratio, framebuffer_size, mouse_capture, .. } = self; let mut handler = Handler { event_queue, close_requested, last_cursor_pos, mouse_delta, - surface, device, config, params, params_buf, queue, content_rect, + modifiers, caps_lock, surface, device, config, params, params_buf, queue, content_rect, aspect_ratio: *aspect_ratio, framebuffer_size: *framebuffer_size, mouse_capture: *mouse_capture, @@ -380,6 +391,8 @@ struct Handler<'a> { close_requested: &'a mut bool, last_cursor_pos: &'a mut Option<(f64, f64)>, mouse_delta: &'a mut (f32, f32), + modifiers: &'a mut ModifiersState, + caps_lock: &'a mut Option, surface: &'a wgpu::Surface<'static>, device: &'a wgpu::Device, config: &'a mut wgpu::SurfaceConfiguration, @@ -415,10 +428,25 @@ impl<'a> ApplicationHandler for Handler<'a> { *self.close_requested = true; self.event_queue.push_back(Event::CloseRequested); } + WindowEvent::ModifiersChanged(m) => *self.modifiers = m.state(), WindowEvent::KeyboardInput { event: KeyEvent { physical_key, state, repeat, text, .. }, .. } => { if let PhysicalKey::Code(code) = physical_key && let Some(key) = from_winit_keycode(code) { + if key == Key::CapsLock && state == ElementState::Pressed && !repeat + && let Some(on) = self.caps_lock + { + *on = !*on; + } + // Buchstabe ohne Modifier: Groß/klein verrät den Capslock-Zustand. + // Nur Tasten A–Z und Zeichen mit Groß/Kleinschreibung (Layout-unabhängig). + if state == ElementState::Pressed && self.modifiers.is_empty() + && (Key::A as u8..=Key::Z as u8).contains(&(key as u8)) + && let Some(c) = text.as_ref().and_then(|t| t.chars().next()) + && (c.is_uppercase() || c.is_lowercase()) + { + *self.caps_lock = Some(c.is_uppercase()); + } self.event_queue.push_back(Event::Key { key, pressed: state == ElementState::Pressed, @@ -521,7 +549,7 @@ fn from_winit_keycode(k: KeyCode) -> Option { K::ArrowUp => Key::Up, K::ArrowDown => Key::Down, K::ArrowLeft => Key::Left, K::ArrowRight => Key::Right, K::Space => Key::Space, K::Enter => Key::Enter, K::Escape => Key::Escape, - K::Tab => Key::Tab, K::Backspace => Key::Backspace, + K::Tab => Key::Tab, K::Backspace => Key::Backspace, K::CapsLock => Key::CapsLock, K::ShiftLeft => Key::LShift, K::ShiftRight => Key::RShift, K::ControlLeft => Key::LCtrl, K::ControlRight => Key::RCtrl, K::AltLeft => Key::LAlt, K::AltRight => Key::RAlt,