Compare commits

...

6 Commits

Author SHA1 Message Date
Manish Goregaokar
e5291c9e28 Release 0.20.0-alpha5 (#1315) 2019-12-09 17:29:50 -07:00
Murarth
35505a3114 X11: Sync key press/release with window focus (#1296)
* X11: Sync key press/release with window focus

* When a window loses focus, key release events are issued for all pressed keys
* When a window gains focus, key press events are issued for all pressed keys
* Adds `is_synthetic` field to `WindowEvent` variant `KeyboardInput`
  to indicate that these events are synthetic.
* Adds `is_synthetic: false` to `WindowEvent::KeyboardInput` events issued
  on all other platforms

* Clarify code with comments
2019-12-07 15:51:37 -07:00
zserik
830d47a5f7 Have EventLoopClosed contain the original event (#1294)
* Fix issue #1292

* Remove "optionally" from changelog entry
2019-12-07 10:22:03 -07:00
Murarth
1a514dff38 X11: Fix incorrect DPI factor when waking from suspend (#1303) 2019-12-04 10:18:20 -07:00
Osspial
2888d5c6cf Fix array_into_iter warning on Windows (#1308) 2019-12-04 12:02:33 -05:00
Osspial
400f75a2b3 Make WindowStore::for_each less terrifying to rebase (#1304) 2019-12-04 03:55:49 -05:00
23 changed files with 310 additions and 133 deletions

View File

@@ -1,5 +1,7 @@
# Unreleased
# # 0.20.0 Alpha 5 (2019-12-09)
- On macOS, fix application termination on `ControlFlow::Exit`
- On Windows, fix missing `ReceivedCharacter` events when Alt is held.
- On macOS, stop emitting private corporate characters in `ReceivedCharacter` events.
@@ -8,6 +10,11 @@
- On X11, fix key modifiers being incorrectly reported.
- On X11, fix window creation hanging when another window is fullscreen.
- On Windows, fix focusing unfocused windows when switching from fullscreen to windowed.
- On X11, fix reporting incorrect DPI factor when waking from suspend.
- Change `EventLoopClosed` to contain the original event.
- Add `is_synthetic` field to `WindowEvent` variant `KeyboardInput`,
indicating that the event is generated by winit.
- On X11, generate synthetic key events for keys held when a window gains or loses focus.
# 0.20.0 Alpha 4 (2019-10-18)

View File

@@ -1,6 +1,6 @@
[package]
name = "winit"
version = "0.20.0-alpha4"
version = "0.20.0-alpha5"
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
description = "Cross-platform window creation library."
edition = "2018"

View File

@@ -132,6 +132,15 @@ pub enum WindowEvent {
KeyboardInput {
device_id: DeviceId,
input: KeyboardInput,
/// If `true`, the event was generated synthetically by winit
/// in one of the following circumstances:
///
/// * **X11**: Synthetic key press events are generated for all keys pressed
/// when a window gains focus. Likewise, synthetic key release events
/// are generated for all keys pressed when a window goes out of focus.
///
/// Otherwise, this value is always `false`.
is_synthetic: bool,
},
/// The cursor has moved on the window.

View File

@@ -199,7 +199,7 @@ impl<T: 'static> EventLoopProxy<T> {
/// function.
///
/// Returns an `Err` if the associated `EventLoop` no longer exists.
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.event_loop_proxy.send_event(event)
}
}
@@ -211,17 +211,17 @@ impl<T: 'static> fmt::Debug for EventLoopProxy<T> {
}
/// The error that is returned when an `EventLoopProxy` attempts to wake up an `EventLoop` that
/// no longer exists.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct EventLoopClosed;
/// no longer exists. Contains the original event given to `send_event`.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct EventLoopClosed<T>(pub T);
impl fmt::Display for EventLoopClosed {
impl<T: fmt::Debug> fmt::Display for EventLoopClosed<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", error::Error::description(self))
}
}
impl error::Error for EventLoopClosed {
impl<T: fmt::Debug> error::Error for EventLoopClosed<T> {
fn description(&self) -> &str {
"Tried to wake up a closed `EventLoop`"
}

View File

@@ -157,7 +157,7 @@ impl EventLoop {
}
impl EventLoopProxy {
pub fn wakeup(&self) -> Result<(), ::EventLoopClosed> {
pub fn wakeup(&self) -> Result<(), ::EventLoopClosed<()>> {
android_glue::wake_event_loop();
Ok(())
}

View File

@@ -165,8 +165,10 @@ impl<T> EventLoopProxy<T> {
}
}
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
self.sender.send(event).map_err(|_| EventLoopClosed)?;
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.sender
.send(event)
.map_err(|::std::sync::mpsc::SendError(x)| EventLoopClosed(x))?;
unsafe {
// let the main thread know there's a new event
CFRunLoopSourceSignal(self.source);

View File

@@ -650,7 +650,7 @@ impl<T: 'static> EventLoop<T> {
}
impl<T: 'static> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
match *self {
EventLoopProxy::Wayland(ref proxy) => proxy.send_event(event),
EventLoopProxy::X(ref proxy) => proxy.send_event(event),

View File

@@ -282,8 +282,14 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
}
impl<T: 'static> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
self.user_sender.send(event).map_err(|_| EventLoopClosed)
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.user_sender.send(event).map_err(|e| {
EventLoopClosed(if let ::calloop::channel::SendError::Disconnected(x) = e {
x
} else {
unreachable!()
})
})
}
}
@@ -664,60 +670,53 @@ impl<T> EventLoop<T> {
}
}
// process pending resize/refresh
window_target.store.lock().unwrap().for_each(
|newsize,
size,
new_dpi,
refresh,
frame_refresh,
closed,
grab_cursor,
surface,
wid,
frame| {
if let Some(frame) = frame {
if let Some(newsize) = newsize {
// Drop resize events equaled to the current size
if newsize != *size {
let (w, h) = newsize;
frame.resize(w, h);
frame.refresh();
let logical_size = crate::dpi::LogicalSize::new(w as f64, h as f64);
sink.send_window_event(
crate::event::WindowEvent::Resized(logical_size),
wid,
);
*size = (w, h);
} else {
// Refresh csd, etc, otherwise
frame.refresh();
}
} else if frame_refresh {
window_target.store.lock().unwrap().for_each(|window| {
if let Some(frame) = window.frame {
if let Some(newsize) = window.newsize {
// Drop resize events equaled to the current size
if newsize != *window.size {
let (w, h) = newsize;
frame.resize(w, h);
frame.refresh();
if !refresh {
frame.surface().commit()
}
let logical_size = crate::dpi::LogicalSize::new(w as f64, h as f64);
sink.send_window_event(
crate::event::WindowEvent::Resized(logical_size),
window.wid,
);
*window.size = (w, h);
} else {
// Refresh csd, etc, otherwise
frame.refresh();
}
} else if window.frame_refresh {
frame.refresh();
if !window.refresh {
frame.surface().commit()
}
}
if let Some(dpi) = new_dpi {
sink.send_window_event(
crate::event::WindowEvent::HiDpiFactorChanged(dpi as f64),
wid,
);
}
if refresh {
sink.send_window_event(crate::event::WindowEvent::RedrawRequested, wid);
}
if closed {
sink.send_window_event(crate::event::WindowEvent::CloseRequested, wid);
}
}
if let Some(dpi) = window.new_dpi {
sink.send_window_event(
crate::event::WindowEvent::HiDpiFactorChanged(dpi as f64),
window.wid,
);
}
if window.refresh {
sink.send_window_event(crate::event::WindowEvent::RedrawRequested, window.wid);
}
if window.closed {
sink.send_window_event(crate::event::WindowEvent::CloseRequested, window.wid);
}
if let Some(grab_cursor) = grab_cursor {
let surface = if grab_cursor { Some(surface) } else { None };
self.cursor_manager.lock().unwrap().grab_pointer(surface);
}
},
)
if let Some(grab_cursor) = window.grab_cursor {
let surface = if grab_cursor {
Some(window.surface)
} else {
None
};
self.cursor_manager.lock().unwrap().grab_pointer(surface);
}
})
}
}

View File

@@ -74,6 +74,7 @@ pub fn init_keyboard(
virtual_keycode: vkcode,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
is_synthetic: false,
},
})
.unwrap();
@@ -125,6 +126,7 @@ pub fn init_keyboard(
virtual_keycode: vkcode,
modifiers: my_modifiers.lock().unwrap().clone(),
},
is_synthetic: false,
},
})
.unwrap();
@@ -198,6 +200,7 @@ pub fn init_keyboard(
virtual_keycode: None,
modifiers: ModifiersState::default(),
},
is_synthetic: false,
},
})
.unwrap();

View File

@@ -385,6 +385,19 @@ pub struct WindowStore {
windows: Vec<InternalWindow>,
}
pub struct WindowStoreForEach<'a> {
pub newsize: Option<(u32, u32)>,
pub size: &'a mut (u32, u32),
pub new_dpi: Option<i32>,
pub refresh: bool,
pub frame_refresh: bool,
pub closed: bool,
pub grab_cursor: Option<bool>,
pub surface: &'a wl_surface::WlSurface,
pub wid: WindowId,
pub frame: Option<&'a mut SWindow<ConceptFrame>>,
}
impl WindowStore {
pub fn new() -> WindowStore {
WindowStore {
@@ -434,34 +447,23 @@ impl WindowStore {
pub fn for_each<F>(&mut self, mut f: F)
where
F: FnMut(
Option<(u32, u32)>,
&mut (u32, u32),
Option<i32>,
bool,
bool,
bool,
Option<bool>,
&wl_surface::WlSurface,
WindowId,
Option<&mut SWindow<ConceptFrame>>,
),
F: FnMut(WindowStoreForEach<'_>),
{
for window in &mut self.windows {
let opt_arc = window.frame.upgrade();
let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap());
f(
window.newsize.take(),
&mut *(window.size.lock().unwrap()),
window.new_dpi,
replace(&mut *window.need_refresh.lock().unwrap(), false),
replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
window.closed,
window.cursor_grab_changed.lock().unwrap().take(),
&window.surface,
make_wid(&window.surface),
opt_mutex_lock.as_mut().map(|m| &mut **m),
);
f(WindowStoreForEach {
newsize: window.newsize.take(),
size: &mut *(window.size.lock().unwrap()),
new_dpi: window.new_dpi,
refresh: replace(&mut *window.need_refresh.lock().unwrap(), false),
frame_refresh: replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
closed: window.closed,
grab_cursor: window.cursor_grab_changed.lock().unwrap().take(),
surface: &window.surface,
wid: make_wid(&window.surface),
frame: opt_mutex_lock.as_mut().map(|m| &mut **m),
});
if let Some(dpi) = window.new_dpi.take() {
window.current_dpi = dpi;
}

View File

@@ -1,4 +1,4 @@
use std::{cell::RefCell, collections::HashMap, ptr, rc::Rc, slice};
use std::{cell::RefCell, collections::HashMap, rc::Rc, slice};
use libc::{c_char, c_int, c_long, c_uint, c_ulong};
@@ -12,7 +12,7 @@ use util::modifiers::{ModifierKeyState, ModifierKeymap};
use crate::{
dpi::{LogicalPosition, LogicalSize},
event::{DeviceEvent, Event, KeyboardInput, ModifiersState, WindowEvent},
event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent},
event_loop::EventLoopWindowTarget as RootELW,
};
@@ -406,14 +406,16 @@ impl<T: 'static> EventProcessor<T> {
let last_hidpi_factor = shared_state_lock.last_monitor.hidpi_factor;
let new_hidpi_factor = {
let window_rect = util::AaRect::new(new_outer_position, new_inner_size);
monitor = wt.xconn.get_monitor_for_window(Some(window_rect));
let new_hidpi_factor = monitor.hidpi_factor;
let new_monitor = wt.xconn.get_monitor_for_window(Some(window_rect));
// Avoid caching an invalid dummy monitor handle
if monitor.id != 0 {
if new_monitor.is_dummy() {
// Avoid updating monitor using a dummy monitor handle
last_hidpi_factor
} else {
monitor = new_monitor;
shared_state_lock.last_monitor = monitor.clone();
monitor.hidpi_factor
}
new_hidpi_factor
};
if last_hidpi_factor != new_hidpi_factor {
events.dpi_changed =
@@ -555,22 +557,13 @@ impl<T: 'static> EventProcessor<T> {
// value, though this should only be an issue under multiseat configurations.
let device = util::VIRTUAL_CORE_KEYBOARD;
let device_id = mkdid(device);
let keycode = xkev.keycode;
// When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with
// a keycode of 0.
if xkev.keycode != 0 {
let keysym = unsafe {
let mut keysym = 0;
(wt.xconn.xlib.XLookupString)(
xkev,
ptr::null_mut(),
0,
&mut keysym,
ptr::null_mut(),
);
wt.xconn.check_errors().expect("Failed to lookup keysym");
keysym
};
if keycode != 0 {
let scancode = keycode - 8;
let keysym = wt.xconn.lookup_keysym(xkev);
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
update_modifiers!(
@@ -586,10 +579,11 @@ impl<T: 'static> EventProcessor<T> {
device_id,
input: KeyboardInput {
state,
scancode: xkev.keycode - 8,
scancode,
virtual_keycode,
modifiers,
},
is_synthetic: false,
},
});
}
@@ -906,6 +900,10 @@ impl<T: 'static> EventProcessor<T> {
event: Focused(true),
});
let modifiers = ModifiersState::from_x11(&xev.mods);
update_modifiers!(modifiers, None);
// The deviceid for this event is for a keyboard instead of a pointer,
// so we have to do a little extra work.
let pointer_id = self
@@ -924,9 +922,12 @@ impl<T: 'static> EventProcessor<T> {
event: CursorMoved {
device_id: mkdid(pointer_id),
position,
modifiers: ModifiersState::from_x11(&xev.mods),
modifiers,
},
});
// Issue key press events for all pressed keys
self.handle_pressed_keys(window_id, ElementState::Pressed, &mut callback);
}
ffi::XI_FocusOut => {
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
@@ -938,8 +939,13 @@ impl<T: 'static> EventProcessor<T> {
.unfocus(xev.event)
.expect("Failed to unfocus input context");
let window_id = mkwid(xev.event);
// Issue key release events for all pressed keys
self.handle_pressed_keys(window_id, ElementState::Released, &mut callback);
callback(Event::WindowEvent {
window_id: mkwid(xev.event),
window_id,
event: Focused(false),
})
}
@@ -1056,20 +1062,8 @@ impl<T: 'static> EventProcessor<T> {
return;
}
let scancode = (keycode - 8) as u32;
let keysym = unsafe {
(wt.xconn.xlib.XKeycodeToKeysym)(
wt.xconn.display,
xev.detail as ffi::KeyCode,
0,
)
};
wt.xconn
.check_errors()
.expect("Failed to lookup raw keysym");
let keysym = wt.xconn.keycode_to_keysym(keycode as ffi::KeyCode);
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
let modifiers = self.device_mod_state.modifiers();
callback(Event::DeviceEvent {
@@ -1180,4 +1174,45 @@ impl<T: 'static> EventProcessor<T> {
Err(_) => (),
}
}
fn handle_pressed_keys<F>(
&self,
window_id: crate::window::WindowId,
state: ElementState,
callback: &mut F,
) where
F: FnMut(Event<T>),
{
let wt = get_xtarget(&self.target);
let device_id = mkdid(util::VIRTUAL_CORE_KEYBOARD);
let modifiers = self.device_mod_state.modifiers();
// Get the set of keys currently pressed and apply Key events to each
let keys = wt.xconn.query_keymap();
for keycode in &keys {
if keycode < 8 {
continue;
}
let scancode = (keycode - 8) as u32;
let keysym = wt.xconn.keycode_to_keysym(keycode);
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
callback(Event::WindowEvent {
window_id,
event: WindowEvent::KeyboardInput {
device_id,
input: KeyboardInput {
scancode,
state,
virtual_keycode,
modifiers,
},
is_synthetic: true,
},
});
}
}
}

View File

@@ -1,4 +1,10 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
#![cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
mod dnd;
mod event_processor;
@@ -425,8 +431,14 @@ impl<T> EventLoopWindowTarget<T> {
}
impl<T: 'static> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
self.user_sender.send(event).map_err(|_| EventLoopClosed)
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.user_sender.send(event).map_err(|e| {
EventLoopClosed(if let ::calloop::channel::SendError::Disconnected(x) = e {
x
} else {
unreachable!()
})
})
}
}

View File

@@ -143,6 +143,11 @@ impl MonitorHandle {
}
}
pub(crate) fn is_dummy(&self) -> bool {
// Zero is an invalid XID value; no real monitor will have it
self.id == 0
}
pub fn name(&self) -> Option<String> {
Some(self.name.clone())
}

View File

@@ -0,0 +1,92 @@
use std::{iter::Enumerate, ptr, slice::Iter};
use super::*;
pub struct Keymap {
keys: [u8; 32],
}
pub struct KeymapIter<'a> {
iter: Enumerate<Iter<'a, u8>>,
index: usize,
item: Option<u8>,
}
impl Keymap {
pub fn iter(&self) -> KeymapIter<'_> {
KeymapIter {
iter: self.keys.iter().enumerate(),
index: 0,
item: None,
}
}
}
impl<'a> IntoIterator for &'a Keymap {
type Item = ffi::KeyCode;
type IntoIter = KeymapIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl Iterator for KeymapIter<'_> {
type Item = ffi::KeyCode;
fn next(&mut self) -> Option<ffi::KeyCode> {
if self.item.is_none() {
while let Some((index, &item)) = self.iter.next() {
if item != 0 {
self.index = index;
self.item = Some(item);
break;
}
}
}
self.item.take().map(|item| {
debug_assert!(item != 0);
let bit = first_bit(item);
if item != bit {
// Remove the first bit; save the rest for further iterations
self.item = Some(item ^ bit);
}
let shift = bit.trailing_zeros() + (self.index * 8) as u32;
shift as ffi::KeyCode
})
}
}
impl XConnection {
pub fn keycode_to_keysym(&self, keycode: ffi::KeyCode) -> ffi::KeySym {
unsafe { (self.xlib.XKeycodeToKeysym)(self.display, keycode, 0) }
}
pub fn lookup_keysym(&self, xkev: &mut ffi::XKeyEvent) -> ffi::KeySym {
let mut keysym = 0;
unsafe {
(self.xlib.XLookupString)(xkev, ptr::null_mut(), 0, &mut keysym, ptr::null_mut());
}
keysym
}
pub fn query_keymap(&self) -> Keymap {
let mut keys = [0; 32];
unsafe {
(self.xlib.XQueryKeymap)(self.display, keys.as_mut_ptr() as *mut c_char);
}
Keymap { keys }
}
}
fn first_bit(b: u8) -> u8 {
1 << b.trailing_zeros()
}

View File

@@ -9,6 +9,7 @@ mod geometry;
mod hint;
mod icon;
mod input;
pub mod keys;
mod memory;
pub mod modifiers;
mod randr;

View File

@@ -651,7 +651,7 @@ impl UnownedWindow {
};
// Don't set fullscreen on an invalid dummy monitor handle
if monitor.id == 0 {
if monitor.is_dummy() {
return None;
}

View File

@@ -264,6 +264,7 @@ pub unsafe fn modifier_event(
virtual_keycode,
modifiers: event_mods(ns_event),
},
is_synthetic: false,
})
} else {
None

View File

@@ -142,8 +142,10 @@ impl<T> Proxy<T> {
}
}
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
self.sender.send(event).map_err(|_| EventLoopClosed)?;
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.sender
.send(event)
.map_err(|mpsc::SendError(x)| EventLoopClosed(x))?;
unsafe {
// let the main thread know there's a new event
CFRunLoopSourceSignal(self.source);

View File

@@ -582,6 +582,7 @@ extern "C" fn key_down(this: &Object, _sel: Sel, event: id) {
virtual_keycode,
modifiers: event_mods(event),
},
is_synthetic: false,
},
};
@@ -633,6 +634,7 @@ extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
virtual_keycode,
modifiers: event_mods(event),
},
is_synthetic: false,
},
};
@@ -741,6 +743,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
virtual_keycode,
modifiers: event_mods(event),
},
is_synthetic: false,
},
};

View File

@@ -11,7 +11,7 @@ impl<T: 'static> Proxy<T> {
Proxy { runner }
}
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
self.runner.send_event(Event::UserEvent(event));
Ok(())
}

View File

@@ -67,6 +67,7 @@ impl<T> WindowTarget<T> {
virtual_keycode,
modifiers,
},
is_synthetic: false,
},
});
});
@@ -83,6 +84,7 @@ impl<T> WindowTarget<T> {
virtual_keycode,
modifiers,
},
is_synthetic: false,
},
});
});

View File

@@ -39,7 +39,7 @@ unsafe fn get_char(keyboard_state: &[u8; 256], v_key: u32, hkl: HKL) -> Option<c
hkl,
);
if len >= 1 {
char::decode_utf16(unicode_bytes.into_iter().cloned())
char::decode_utf16(unicode_bytes.iter().cloned())
.next()
.and_then(|c| c.ok())
} else {

View File

@@ -742,13 +742,13 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
}
impl<T: 'static> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
unsafe {
if winuser::PostMessageW(self.target_window, *USER_EVENT_MSG_ID, 0, 0) != 0 {
self.event_send.send(event).ok();
Ok(())
} else {
Err(EventLoopClosed)
Err(EventLoopClosed(event))
}
}
}
@@ -1277,6 +1277,7 @@ unsafe extern "system" fn public_window_callback<T>(
virtual_keycode: vkey,
modifiers: event::get_key_mods(),
},
is_synthetic: false,
},
});
// Windows doesn't emit a delete character by default, but in order to make it
@@ -1305,6 +1306,7 @@ unsafe extern "system" fn public_window_callback<T>(
virtual_keycode: vkey,
modifiers: event::get_key_mods(),
},
is_synthetic: false,
},
});
}