Winit: Multiple `DeviceEvent` variants not working (Wayland)

Created on 29 May 2020  路  6Comments  路  Source: rust-windowing/winit

This is my event loop:

        event_loop.run(move |event, _, control_flow| {
            *control_flow = ControlFlow::Poll;
            match event {
                Event::WindowEvent {
                    ref event,
                    window_id,
                } if window_id == self.window.id() => match event {
                    WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
                    WindowEvent::KeyboardInput { input, .. } => {
                        if let KeyboardInput {
                            state: ElementState::Pressed,
                            virtual_keycode: Some(VirtualKeyCode::Escape),
                            ..
                        } = input
                        {
                            // exit on <esc>
                            *control_flow = ControlFlow::Exit;
                        }
                    }
                    WindowEvent::Resized(physical_size) => {
                        self.resize(*physical_size);
                    }
                    WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
                        // new_inner_size is &mut, so we have to dereference it twice
                        self.resize(**new_inner_size);
                    }
                    _ => (),
                },
                Event::MainEventsCleared => {
                    let elapsed = self.last_update_time.elapsed().as_secs_f32();
                    if elapsed >= 1.0 / self.fps {
                        // only request rendering if something was updated
                        if self.logic(elapsed) {
                            self.window.request_redraw();
                        }

                        self.last_update_time = Instant::now();
                    } else {
                        // sleep until the next update. NOTE: this might be bad, so remove if there are
                        // problems.
                        std::thread::sleep(std::time::Duration::from_secs_f32(
                            1.0 / self.fps - elapsed,
                        ));
                    }
                }
                Event::RedrawRequested(_) => {
                    self.render();
                    frame_count += 1;

                    // report fps
                    let elapsed = last_fps_report.elapsed();
                    if elapsed >= std::time::Duration::from_secs(1) {
                        println!("{} fps", frame_count as f32 / elapsed.as_secs_f32());
                        frame_count = 0;
                        last_fps_report = Instant::now();
                    }
                }
                Event::DeviceEvent { event, .. } => {
                    println!("event: {:#?}", event); // <----- !!!
                    if !self.input(&event) {
                        if let DeviceEvent::Key(input) = event {
                            if let ElementState::Pressed = input.state {
                                if let Some(VirtualKeyCode::Escape) = input.virtual_keycode {
                                    *control_flow = ControlFlow::Exit;
                                }
                            }
                        }
                    }
                }
                _ => (),
            }
        })

Where Event::DeviceEvent is matched, the println never outputs DeviceEvent::Key or DeviceEvent::Text events. However, ModifiersChanged and MouseMotion seem to work. In fact, those are the only two variants that work.

But I'm able to capture keyboard events with WindowEvent.

_Edit: winit version 0.20_

Wayland question

All 6 comments

That could be true because of how Wayland works in general. What are you expecting from device events? Is your goal some global hotkey, logger? Use window events, etc.

I'm making a game, so the hope is to use these events to move the player, camera angle, etc:

    pub fn input(&mut self, event: &DeviceEvent) -> bool {
        match event {
            DeviceEvent::MouseMotion { delta: (x, y) } => {
                self.rotation.x = self.rotation.y + (*x as f32 * 10.0 * self.mouse_sensitivity);
                self.rotation.y = (self.rotation.x + (*y as f32 * 10.0 * self.mouse_sensitivity)).min(90.0).max(-90.0);
                true
            }
            DeviceEvent::Key(KeyboardInput {
                state,
                virtual_keycode: Some(keycode),
                ..
            }) => {
                let is_pressed = *state == ElementState::Pressed;
                match keycode {
                    VirtualKeyCode::Space => {
                        self.is_up_pressed = is_pressed;
                        true
                    }
                    VirtualKeyCode::LShift => {
                        self.is_down_pressed = is_pressed;
                        true
                    }
                    VirtualKeyCode::W | VirtualKeyCode::Up => {
                        self.is_forward_pressed = is_pressed;
                        true
                    }
                    VirtualKeyCode::A | VirtualKeyCode::Left => {
                        self.is_left_pressed = is_pressed;
                        true
                    }
                    VirtualKeyCode::S | VirtualKeyCode::Down => {
                        self.is_backward_pressed = is_pressed;
                        true
                    }
                    VirtualKeyCode::D | VirtualKeyCode::Right => {
                        self.is_right_pressed = is_pressed;
                        true
                    }
                    _ => false,
                }
            }
            _ => false,
        }
    }

I'm making a game, so the hope is to use these events to move the player, camera angle, etc:

Yeah, use window events. They all the same but for your window, not for the entire system, like global events.

Ah okay :) Should've known it would work better! Thanks for the pointers :)

The idea behind the device events is that they are global for your system. Like if you type in some other app your app will see that as well, however window events are just for your window. But Wayland is special and you don't have information about events for other windows at all, as well as modifiers(On latest winit it's a window event btw). So everything is in some kind of isolation from each other and doesn't know about each other.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

felixrabe picture felixrabe  路  4Comments

swiftcoder picture swiftcoder  路  3Comments

chrisduerr picture chrisduerr  路  3Comments

e00E picture e00E  路  5Comments

dhardy picture dhardy  路  3Comments