Changed backend from glfw to winit
This commit is contained in:
+1
-1
@@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
glfw = "0.60.0"
|
winit = "0.30"
|
||||||
wgpu = "27"
|
wgpu = "27"
|
||||||
pollster = "0.4.0"
|
pollster = "0.4.0"
|
||||||
bytemuck = { version = "1.24.0", features = ["derive"] }
|
bytemuck = { version = "1.24.0", features = ["derive"] }
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# pbio: simple pixel buffer and I/O system
|
# pbio: simple pixel buffer and I/O system
|
||||||
|
|
||||||
Pixelbuffer-basiertes Fenster mit Palette-Lookup-Renderer (GLFW + wgpu) und Maus/Tastatureingaben.
|
Pixelbuffer-basiertes Fenster mit Palette-Lookup-Renderer (winit + wgpu) und Maus/Tastatureingaben.
|
||||||
|
|
||||||
pbio nimmt einen Bytearray, interpretiert diesen als 8-Bit-Pixelbuffer und rendert ihn auf ein Fenster. Optional mit Letterbox für ein festes Seitenverhältnis
|
pbio nimmt einen Bytearray, interpretiert diesen als 8-Bit-Pixelbuffer und rendert ihn auf ein Fenster. Optional mit Letterbox für ein festes Seitenverhältnis.
|
||||||
|
|
||||||
## Integration
|
## Integration
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ pbio ist nicht auf crates.io veröffentlicht. Das Crate kann direkt aus dem Git-
|
|||||||
pbio = { git = "https://codeberg.org/irrlicht/rust-pbio.git" }
|
pbio = { git = "https://codeberg.org/irrlicht/rust-pbio.git" }
|
||||||
```
|
```
|
||||||
|
|
||||||
pbio benötigt eine funktionierende GLFW-Toolchain (CMake + C-Compiler) zum Bauen sowie aktuelle Grafiktreiber mit Vulkan-, Metal- oder DX12-Unterstützung für wgpu.
|
pbio braucht keine System-Libraries zum Bauen — winit lädt X11/Wayland zur Laufzeit per dlopen. Zur Laufzeit reichen aktuelle Grafiktreiber mit Vulkan-, Metal- oder DX12-Unterstützung für wgpu.
|
||||||
|
|
||||||
## Verwendung
|
## Verwendung
|
||||||
|
|
||||||
|
|||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
with open("rgb332.gpl", "w") as f:
|
||||||
|
f.write("GIMP Palette\nName: RGB332\nColumns: 16\n#\n")
|
||||||
|
for i in range(256):
|
||||||
|
r = ((i >> 5) * 255) // 7
|
||||||
|
g = (((i >> 2) & 0x7) * 255) // 7
|
||||||
|
b = ((i & 0x3) * 255) // 3
|
||||||
|
f.write(f"{r:3d} {g:3d} {b:3d} Index {i}\n")
|
||||||
+206
-102
@@ -1,9 +1,19 @@
|
|||||||
//! pbio — Pixelbuffer-basiertes Fenster mit Palette-Lookup-Renderer.
|
//! pbio — Pixelbuffer-basiertes Fenster mit Palette-Lookup-Renderer.
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
use winit::application::ApplicationHandler;
|
||||||
|
use winit::dpi::LogicalSize;
|
||||||
|
use winit::event::{
|
||||||
|
DeviceEvent, DeviceId, ElementState, KeyEvent, MouseButton as WinitMouseButton, WindowEvent,
|
||||||
|
};
|
||||||
|
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||||
|
use winit::keyboard::{KeyCode, PhysicalKey};
|
||||||
|
use winit::platform::pump_events::EventLoopExtPumpEvents;
|
||||||
|
use winit::window::{CursorGrabMode, Window, WindowAttributes, WindowId};
|
||||||
|
|
||||||
pub mod palette;
|
pub mod palette;
|
||||||
|
|
||||||
@@ -78,7 +88,7 @@ struct Params {
|
|||||||
// --- Platform --------------------------------------------------------------
|
// --- Platform --------------------------------------------------------------
|
||||||
|
|
||||||
pub struct Platform {
|
pub struct Platform {
|
||||||
// GPU-Ressourcen vor Surface, Surface vor Window/Glfw (Drop-Reihenfolge).
|
// GPU-Ressourcen vor Surface, Surface vor Window (Drop-Reihenfolge).
|
||||||
bind_group: wgpu::BindGroup,
|
bind_group: wgpu::BindGroup,
|
||||||
pipeline: wgpu::RenderPipeline,
|
pipeline: wgpu::RenderPipeline,
|
||||||
index_tex: wgpu::Texture,
|
index_tex: wgpu::Texture,
|
||||||
@@ -93,22 +103,20 @@ pub struct Platform {
|
|||||||
queue: wgpu::Queue,
|
queue: wgpu::Queue,
|
||||||
instance: wgpu::Instance,
|
instance: wgpu::Instance,
|
||||||
|
|
||||||
window: glfw::PWindow,
|
window: Arc<Window>,
|
||||||
events: glfw::GlfwReceiver<(f64, glfw::WindowEvent)>,
|
event_loop: EventLoop<()>,
|
||||||
glfw: glfw::Glfw,
|
|
||||||
|
|
||||||
framebuffer: Vec<u8>,
|
framebuffer: Vec<u8>,
|
||||||
framebuffer_size: (u32, u32),
|
framebuffer_size: (u32, u32),
|
||||||
aspect_ratio: f32, // 0.0 = keine Letterbox
|
aspect_ratio: f32, // 0.0 = keine Letterbox
|
||||||
content_rect: (u32, u32, u32, u32),
|
content_rect: (u32, u32, u32, u32),
|
||||||
event_queue: VecDeque<Event>,
|
event_queue: VecDeque<Event>,
|
||||||
event_scratch: Vec<glfw::WindowEvent>,
|
|
||||||
close_requested: bool,
|
close_requested: bool,
|
||||||
|
|
||||||
mouse_visible: bool,
|
mouse_visible: bool,
|
||||||
mouse_capture: bool,
|
mouse_capture: bool,
|
||||||
// Letzte rohe (window-space) Cursorposition, um Deltas zu bilden. None nach
|
// Letzte rohe (window-space) Cursorposition, um Deltas zu bilden. None nach
|
||||||
// Mode-Wechsel/erstem Frame, damit der GLFW-Recenter keinen Riesensprung erzeugt.
|
// Mode-Wechsel/erstem Frame, damit ein Cursor-Sprung keinen Riesen-Delta erzeugt.
|
||||||
last_cursor_pos: Option<(f64, f64)>,
|
last_cursor_pos: Option<(f64, f64)>,
|
||||||
mouse_delta: (f32, f32),
|
mouse_delta: (f32, f32),
|
||||||
}
|
}
|
||||||
@@ -119,29 +127,27 @@ impl Platform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn new_async(cfg: PlatformConfig) -> Self {
|
async fn new_async(cfg: PlatformConfig) -> Self {
|
||||||
use glfw::fail_on_errors;
|
let mut event_loop = EventLoop::builder().build().expect("event loop");
|
||||||
|
|
||||||
let mut glfw = glfw::init(fail_on_errors!()).unwrap();
|
let attrs = WindowAttributes::default()
|
||||||
glfw.window_hint(glfw::WindowHint::ClientApi(glfw::ClientApiHint::NoApi));
|
.with_title(cfg.title.clone())
|
||||||
|
.with_inner_size(LogicalSize::new(cfg.window_size.0, cfg.window_size.1));
|
||||||
|
|
||||||
let (mut window, events) = glfw
|
// winit 0.30 erlaubt Fenster-Erstellung nur in ApplicationHandler::resumed.
|
||||||
.create_window(cfg.window_size.0, cfg.window_size.1, &cfg.title, glfw::WindowMode::Windowed)
|
// Wir pumpen, bis das Bootstrap-Handler das Window angelegt hat.
|
||||||
.expect("failed to create GLFW window");
|
let mut bootstrap = Bootstrap { attrs, window: None };
|
||||||
|
while bootstrap.window.is_none() {
|
||||||
|
event_loop.pump_app_events(Some(Duration::from_millis(10)), &mut bootstrap);
|
||||||
|
}
|
||||||
|
let window = bootstrap.window.unwrap();
|
||||||
|
|
||||||
window.set_framebuffer_size_polling(true);
|
let win_size = window.inner_size();
|
||||||
window.set_key_polling(true);
|
|
||||||
window.set_char_polling(true);
|
|
||||||
window.set_cursor_pos_polling(true);
|
|
||||||
window.set_mouse_button_polling(true);
|
|
||||||
window.set_close_polling(true);
|
|
||||||
|
|
||||||
let win_size = window.get_framebuffer_size();
|
|
||||||
|
|
||||||
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
|
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
|
||||||
backends: wgpu::Backends::all(),
|
backends: wgpu::Backends::all(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
let surface = instance.create_surface(window.render_context()).expect("surface");
|
let surface = instance.create_surface(window.clone()).expect("surface");
|
||||||
|
|
||||||
let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
|
let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||||||
compatible_surface: Some(&surface),
|
compatible_surface: Some(&surface),
|
||||||
@@ -156,15 +162,26 @@ impl Platform {
|
|||||||
let caps = surface.get_capabilities(&adapter);
|
let caps = surface.get_capabilities(&adapter);
|
||||||
let format = caps.formats.iter().copied().find(|f| f.is_srgb()).unwrap_or(caps.formats[0]);
|
let format = caps.formats.iter().copied().find(|f| f.is_srgb()).unwrap_or(caps.formats[0]);
|
||||||
|
|
||||||
|
// Mailbox/Immediate sind nicht überall in caps.present_modes — Fifo ist
|
||||||
|
// der einzige garantierte Mode.
|
||||||
|
let present_mode = if cfg.vsync {
|
||||||
|
if caps.present_modes.contains(&wgpu::PresentMode::Mailbox) {
|
||||||
|
wgpu::PresentMode::Mailbox
|
||||||
|
} else {
|
||||||
|
wgpu::PresentMode::Fifo
|
||||||
|
}
|
||||||
|
} else if caps.present_modes.contains(&wgpu::PresentMode::Immediate) {
|
||||||
|
wgpu::PresentMode::Immediate
|
||||||
|
} else {
|
||||||
|
wgpu::PresentMode::Fifo
|
||||||
|
};
|
||||||
|
|
||||||
let surface_config = wgpu::SurfaceConfiguration {
|
let surface_config = wgpu::SurfaceConfiguration {
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
format,
|
format,
|
||||||
width: win_size.0 as u32,
|
width: win_size.width,
|
||||||
height: win_size.1 as u32,
|
height: win_size.height,
|
||||||
// TODO: Mailbox ist nicht überall in caps.present_modes — bei
|
present_mode,
|
||||||
// unsupported Treibern panict configure(). Fifo wäre der sichere
|
|
||||||
// Vsync-Fallback. Erstmal beobachten.
|
|
||||||
present_mode: if cfg.vsync { wgpu::PresentMode::Mailbox } else { wgpu::PresentMode::Immediate },
|
|
||||||
alpha_mode: caps.alpha_modes[0],
|
alpha_mode: caps.alpha_modes[0],
|
||||||
view_formats: vec![],
|
view_formats: vec![],
|
||||||
desired_maximum_frame_latency: 2,
|
desired_maximum_frame_latency: 2,
|
||||||
@@ -220,12 +237,11 @@ impl Platform {
|
|||||||
palette: cfg.palette, palette_dirty: false,
|
palette: cfg.palette, palette_dirty: false,
|
||||||
params_buf, params,
|
params_buf, params,
|
||||||
surface, config: surface_config, device, queue, instance,
|
surface, config: surface_config, device, queue, instance,
|
||||||
window, events, glfw,
|
window, event_loop,
|
||||||
framebuffer: vec![0u8; (src_w * src_h) as usize],
|
framebuffer: vec![0u8; (src_w * src_h) as usize],
|
||||||
framebuffer_size: (src_w, src_h),
|
framebuffer_size: (src_w, src_h),
|
||||||
aspect_ratio, content_rect,
|
aspect_ratio, content_rect,
|
||||||
event_queue: VecDeque::new(),
|
event_queue: VecDeque::new(),
|
||||||
event_scratch: Vec::new(),
|
|
||||||
close_requested: false,
|
close_requested: false,
|
||||||
mouse_visible: cfg.mouse_visible,
|
mouse_visible: cfg.mouse_visible,
|
||||||
mouse_capture: cfg.mouse_capture,
|
mouse_capture: cfg.mouse_capture,
|
||||||
@@ -249,36 +265,46 @@ impl Platform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn apply_cursor_mode(&mut self) {
|
fn apply_cursor_mode(&mut self) {
|
||||||
// capture impliziert hidden — GLFW kennt keinen "locked + visible".
|
if self.mouse_capture {
|
||||||
let mode = match (self.mouse_capture, self.mouse_visible) {
|
// Wayland kann Locked, X11/Windows fallen auf Confined zurück.
|
||||||
(true, _) => glfw::CursorMode::Disabled,
|
let r = self.window.set_cursor_grab(CursorGrabMode::Locked)
|
||||||
(false, true) => glfw::CursorMode::Normal,
|
.or_else(|_| self.window.set_cursor_grab(CursorGrabMode::Confined));
|
||||||
(false, false) => glfw::CursorMode::Hidden,
|
if let Err(e) = r {
|
||||||
};
|
eprintln!("pbio: cursor grab failed: {:?}", e);
|
||||||
self.window.set_cursor_mode(mode);
|
}
|
||||||
|
self.window.set_cursor_visible(false);
|
||||||
|
} else {
|
||||||
|
let _ = self.window.set_cursor_grab(CursorGrabMode::None);
|
||||||
|
self.window.set_cursor_visible(self.mouse_visible);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Akkumulierte Mausbewegung seit dem letzten Aufruf, in Framebuffer-Pixeln
|
/// Akkumulierte Mausbewegung seit dem letzten Aufruf, in Framebuffer-Pixeln
|
||||||
/// (gleiche Skala wie `Event::MouseMove`). Bei `set_mouse_capture(true)`
|
/// (gleiche Skala wie `Event::MouseMove`). Bei `set_mouse_capture(true)`
|
||||||
/// liefert GLFW unbegrenzte Deltas (FPS-Stil).
|
/// kommen die Werte aus rohen DeviceEvents (unbegrenzt, FPS-Stil).
|
||||||
pub fn mouse_delta(&mut self) -> (f32, f32) {
|
pub fn mouse_delta(&mut self) -> (f32, f32) {
|
||||||
std::mem::replace(&mut self.mouse_delta, (0.0, 0.0))
|
std::mem::replace(&mut self.mouse_delta, (0.0, 0.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn should_close(&self) -> bool { self.close_requested || self.window.should_close() }
|
pub fn should_close(&self) -> bool { self.close_requested }
|
||||||
pub fn request_close(&mut self) { self.close_requested = true; self.window.set_should_close(true); }
|
pub fn request_close(&mut self) { self.close_requested = true; }
|
||||||
|
|
||||||
pub fn poll_events(&mut self, timeout: Option<Duration>) {
|
pub fn poll_events(&mut self, timeout: Option<Duration>) {
|
||||||
match timeout {
|
// Felder destructuren, um disjoint &mut-Borrows in den Handler zu reichen.
|
||||||
Some(t) => self.glfw.wait_events_timeout(t.as_secs_f64()),
|
let Self {
|
||||||
None => self.glfw.wait_events(),
|
event_loop, event_queue, close_requested, last_cursor_pos, mouse_delta,
|
||||||
}
|
surface, device, config, params, params_buf, queue, content_rect,
|
||||||
// flush_messages borrowt &events, handle_glfw_event will &mut self → in
|
aspect_ratio, framebuffer_size, mouse_capture, ..
|
||||||
// wiederverwendeten Scratch-Buffer umparken statt pro Frame allokieren.
|
} = self;
|
||||||
let mut scratch = std::mem::take(&mut self.event_scratch);
|
|
||||||
scratch.extend(glfw::flush_messages(&self.events).map(|(_, ev)| ev));
|
let mut handler = Handler {
|
||||||
for ev in scratch.drain(..) { self.handle_glfw_event(ev); }
|
event_queue, close_requested, last_cursor_pos, mouse_delta,
|
||||||
self.event_scratch = scratch;
|
surface, device, config, params, params_buf, queue, content_rect,
|
||||||
|
aspect_ratio: *aspect_ratio,
|
||||||
|
framebuffer_size: *framebuffer_size,
|
||||||
|
mouse_capture: *mouse_capture,
|
||||||
|
};
|
||||||
|
event_loop.pump_app_events(timeout, &mut handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drain_events(&mut self) -> impl Iterator<Item = Event> + '_ {
|
pub fn drain_events(&mut self) -> impl Iterator<Item = Event> + '_ {
|
||||||
@@ -328,7 +354,7 @@ impl Platform {
|
|||||||
let drawable = match self.surface.get_current_texture() {
|
let drawable = match self.surface.get_current_texture() {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
|
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
|
||||||
self.surface = self.instance.create_surface(self.window.render_context()).expect("recreate surface");
|
self.surface = self.instance.create_surface(self.window.clone()).expect("recreate surface");
|
||||||
self.surface.configure(&self.device, &self.config);
|
self.surface.configure(&self.device, &self.config);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -363,57 +389,126 @@ impl Platform {
|
|||||||
self.queue.submit([encoder.finish()]);
|
self.queue.submit([encoder.finish()]);
|
||||||
drawable.present();
|
drawable.present();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_glfw_event(&mut self, ev: glfw::WindowEvent) {
|
// --- Bootstrap-Handler (nur fürs initiale Window-Create) -------------------
|
||||||
use glfw::WindowEvent as W;
|
|
||||||
|
struct Bootstrap {
|
||||||
|
attrs: WindowAttributes,
|
||||||
|
window: Option<Arc<Window>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApplicationHandler for Bootstrap {
|
||||||
|
fn resumed(&mut self, el: &ActiveEventLoop) {
|
||||||
|
if self.window.is_none() {
|
||||||
|
let w = el.create_window(self.attrs.clone()).expect("create_window");
|
||||||
|
self.window = Some(Arc::new(w));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn window_event(&mut self, _: &ActiveEventLoop, _: WindowId, _: WindowEvent) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Laufender Event-Handler (pro pump_events neu aufgebaut) ---------------
|
||||||
|
|
||||||
|
struct Handler<'a> {
|
||||||
|
event_queue: &'a mut VecDeque<Event>,
|
||||||
|
close_requested: &'a mut bool,
|
||||||
|
last_cursor_pos: &'a mut Option<(f64, f64)>,
|
||||||
|
mouse_delta: &'a mut (f32, f32),
|
||||||
|
surface: &'a wgpu::Surface<'static>,
|
||||||
|
device: &'a wgpu::Device,
|
||||||
|
config: &'a mut wgpu::SurfaceConfiguration,
|
||||||
|
params: &'a mut Params,
|
||||||
|
params_buf: &'a wgpu::Buffer,
|
||||||
|
queue: &'a wgpu::Queue,
|
||||||
|
content_rect: &'a mut (u32, u32, u32, u32),
|
||||||
|
aspect_ratio: f32,
|
||||||
|
framebuffer_size: (u32, u32),
|
||||||
|
mouse_capture: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ApplicationHandler for Handler<'a> {
|
||||||
|
fn resumed(&mut self, _: &ActiveEventLoop) {}
|
||||||
|
|
||||||
|
fn window_event(&mut self, _: &ActiveEventLoop, _: WindowId, ev: WindowEvent) {
|
||||||
match ev {
|
match ev {
|
||||||
W::FramebufferSize(w, h) => {
|
WindowEvent::Resized(size) => {
|
||||||
|
let (w, h) = (size.width, size.height);
|
||||||
if w > 0 && h > 0 {
|
if w > 0 && h > 0 {
|
||||||
self.config.width = w as u32;
|
self.config.width = w;
|
||||||
self.config.height = h as u32;
|
self.config.height = h;
|
||||||
self.surface.configure(&self.device, &self.config);
|
self.surface.configure(self.device, self.config);
|
||||||
self.content_rect = compute_content_rect((self.config.width, self.config.height), self.aspect_ratio);
|
*self.content_rect = compute_content_rect((w, h), self.aspect_ratio);
|
||||||
let (x, y, cw, ch) = self.content_rect;
|
let (x, y, cw, ch) = *self.content_rect;
|
||||||
self.params.content_offset = [x as f32, y as f32];
|
self.params.content_offset = [x as f32, y as f32];
|
||||||
self.params.content_size = [cw as f32, ch as f32];
|
self.params.content_size = [cw as f32, ch as f32];
|
||||||
self.queue.write_buffer(&self.params_buf, 0, bytemuck::bytes_of(&self.params));
|
self.queue.write_buffer(self.params_buf, 0, bytemuck::bytes_of(self.params));
|
||||||
}
|
}
|
||||||
self.event_queue.push_back(Event::Resize { width: w.max(0) as u32, height: h.max(0) as u32 });
|
self.event_queue.push_back(Event::Resize { width: w, height: h });
|
||||||
}
|
}
|
||||||
W::Key(k, _, action, _) => if let Some(key) = from_glfw_key(k) {
|
WindowEvent::CloseRequested => {
|
||||||
self.event_queue.push_back(Event::Key {
|
*self.close_requested = true;
|
||||||
key,
|
self.event_queue.push_back(Event::CloseRequested);
|
||||||
pressed: action != glfw::Action::Release,
|
|
||||||
repeat: action == glfw::Action::Repeat,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
W::Char(c) => self.event_queue.push_back(Event::Char(c)),
|
WindowEvent::KeyboardInput { event: KeyEvent { physical_key, state, repeat, text, .. }, .. } => {
|
||||||
W::CursorPos(x, y) => {
|
if let PhysicalKey::Code(code) = physical_key
|
||||||
let (cx, cy, cw, ch) = self.content_rect;
|
&& let Some(key) = from_winit_keycode(code)
|
||||||
|
{
|
||||||
|
self.event_queue.push_back(Event::Key {
|
||||||
|
key,
|
||||||
|
pressed: state == ElementState::Pressed,
|
||||||
|
repeat,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if state == ElementState::Pressed
|
||||||
|
&& let Some(t) = text
|
||||||
|
{
|
||||||
|
for c in t.chars() {
|
||||||
|
self.event_queue.push_back(Event::Char(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::CursorMoved { position, .. } => {
|
||||||
|
let (x, y) = (position.x, position.y);
|
||||||
|
let (cx, cy, cw, ch) = *self.content_rect;
|
||||||
let (fw, fh) = self.framebuffer_size;
|
let (fw, fh) = self.framebuffer_size;
|
||||||
let sx = fw as f64 / cw as f64;
|
let sx = fw as f64 / cw.max(1) as f64;
|
||||||
let sy = fh as f64 / ch as f64;
|
let sy = fh as f64 / ch.max(1) as f64;
|
||||||
|
|
||||||
if let Some((lx, ly)) = self.last_cursor_pos {
|
// Im Capture-Mode liefert winit geclampte CursorMoved-Werte;
|
||||||
self.mouse_delta.0 += ((x - lx) * sx) as f32;
|
// den Delta-Pfad nehmen wir dort aus DeviceEvent::MouseMotion.
|
||||||
self.mouse_delta.1 += ((y - ly) * sy) as f32;
|
if !self.mouse_capture {
|
||||||
|
if let Some((lx, ly)) = *self.last_cursor_pos {
|
||||||
|
self.mouse_delta.0 += ((x - lx) * sx) as f32;
|
||||||
|
self.mouse_delta.1 += ((y - ly) * sy) as f32;
|
||||||
|
}
|
||||||
|
*self.last_cursor_pos = Some((x, y));
|
||||||
}
|
}
|
||||||
self.last_cursor_pos = Some((x, y));
|
|
||||||
|
|
||||||
let fx = ((x - cx as f64) * sx) as f32;
|
let fx = ((x - cx as f64) * sx) as f32;
|
||||||
let fy = ((y - cy as f64) * sy) as f32;
|
let fy = ((y - cy as f64) * sy) as f32;
|
||||||
self.event_queue.push_back(Event::MouseMove { x: fx, y: fy });
|
self.event_queue.push_back(Event::MouseMove { x: fx, y: fy });
|
||||||
}
|
}
|
||||||
W::MouseButton(btn, action, _) => if let Some(button) = from_glfw_button(btn) {
|
WindowEvent::MouseInput { button, state, .. } => if let Some(b) = from_winit_button(button) {
|
||||||
self.event_queue.push_back(Event::MouseBtn { button, pressed: action != glfw::Action::Release });
|
self.event_queue.push_back(Event::MouseBtn {
|
||||||
}
|
button: b,
|
||||||
W::Close => {
|
pressed: state == ElementState::Pressed,
|
||||||
self.close_requested = true;
|
});
|
||||||
self.event_queue.push_back(Event::CloseRequested);
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn device_event(&mut self, _: &ActiveEventLoop, _: DeviceId, ev: DeviceEvent) {
|
||||||
|
if self.mouse_capture && let DeviceEvent::MouseMotion { delta: (dx, dy) } = ev {
|
||||||
|
let (_, _, cw, ch) = *self.content_rect;
|
||||||
|
let (fw, fh) = self.framebuffer_size;
|
||||||
|
let sx = fw as f64 / cw.max(1) as f64;
|
||||||
|
let sy = fh as f64 / ch.max(1) as f64;
|
||||||
|
self.mouse_delta.0 += (dx * sx) as f32;
|
||||||
|
self.mouse_delta.1 += (dy * sy) as f32;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Helpers ---------------------------------------------------------------
|
// --- Helpers ---------------------------------------------------------------
|
||||||
@@ -442,30 +537,39 @@ fn make_tex(device: &wgpu::Device, label: &str, w: u32, h: u32, format: wgpu::Te
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_glfw_key(k: glfw::Key) -> Option<Key> {
|
fn from_winit_keycode(k: KeyCode) -> Option<Key> {
|
||||||
use glfw::Key as G;
|
use KeyCode as K;
|
||||||
macro_rules! same { ($($n:ident),* $(,)?) => { match k {
|
Some(match k {
|
||||||
$(G::$n => Some(Key::$n),)*
|
K::KeyA => Key::A, K::KeyB => Key::B, K::KeyC => Key::C, K::KeyD => Key::D,
|
||||||
G::LeftShift => Some(Key::LShift), G::RightShift => Some(Key::RShift),
|
K::KeyE => Key::E, K::KeyF => Key::F, K::KeyG => Key::G, K::KeyH => Key::H,
|
||||||
G::LeftControl => Some(Key::LCtrl), G::RightControl => Some(Key::RCtrl),
|
K::KeyI => Key::I, K::KeyJ => Key::J, K::KeyK => Key::K, K::KeyL => Key::L,
|
||||||
G::LeftAlt => Some(Key::LAlt), G::RightAlt => Some(Key::RAlt),
|
K::KeyM => Key::M, K::KeyN => Key::N, K::KeyO => Key::O, K::KeyP => Key::P,
|
||||||
_ => None,
|
K::KeyQ => Key::Q, K::KeyR => Key::R, K::KeyS => Key::S, K::KeyT => Key::T,
|
||||||
}}; }
|
K::KeyU => Key::U, K::KeyV => Key::V, K::KeyW => Key::W, K::KeyX => Key::X,
|
||||||
same!(
|
K::KeyY => Key::Y, K::KeyZ => Key::Z,
|
||||||
A, B, C, D, E, F, G, H, I, J, K, L, M,
|
K::Digit0 => Key::Num0, K::Digit1 => Key::Num1, K::Digit2 => Key::Num2,
|
||||||
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
K::Digit3 => Key::Num3, K::Digit4 => Key::Num4, K::Digit5 => Key::Num5,
|
||||||
Num0, Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9,
|
K::Digit6 => Key::Num6, K::Digit7 => Key::Num7, K::Digit8 => Key::Num8,
|
||||||
Up, Down, Left, Right,
|
K::Digit9 => Key::Num9,
|
||||||
Space, Enter, Escape, Tab, Backspace,
|
K::ArrowUp => Key::Up, K::ArrowDown => Key::Down,
|
||||||
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
|
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::ShiftLeft => Key::LShift, K::ShiftRight => Key::RShift,
|
||||||
|
K::ControlLeft => Key::LCtrl, K::ControlRight => Key::RCtrl,
|
||||||
|
K::AltLeft => Key::LAlt, K::AltRight => Key::RAlt,
|
||||||
|
K::F1 => Key::F1, K::F2 => Key::F2, K::F3 => Key::F3, K::F4 => Key::F4,
|
||||||
|
K::F5 => Key::F5, K::F6 => Key::F6, K::F7 => Key::F7, K::F8 => Key::F8,
|
||||||
|
K::F9 => Key::F9, K::F10 => Key::F10, K::F11 => Key::F11, K::F12 => Key::F12,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_glfw_button(b: glfw::MouseButton) -> Option<MouseButton> {
|
fn from_winit_button(b: WinitMouseButton) -> Option<MouseButton> {
|
||||||
match b {
|
match b {
|
||||||
glfw::MouseButton::Button1 => Some(MouseButton::Left),
|
WinitMouseButton::Left => Some(MouseButton::Left),
|
||||||
glfw::MouseButton::Button2 => Some(MouseButton::Right),
|
WinitMouseButton::Right => Some(MouseButton::Right),
|
||||||
glfw::MouseButton::Button3 => Some(MouseButton::Middle),
|
WinitMouseButton::Middle => Some(MouseButton::Middle),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user