1
0
Fork 0

Merge pull request #39 from glowcoil/window

API refactors: pass Window to AppWindow and introduce WindowHandle
This commit is contained in:
william light 2020-09-11 15:27:58 +02:00 committed by GitHub
commit 23af18020e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 199 additions and 228 deletions

View file

@ -1,6 +1,4 @@
use std::sync::mpsc; use baseview::{Event, Window, WindowHandler};
use baseview::Event;
fn main() { fn main() {
let window_open_options = baseview::WindowOpenOptions { let window_open_options = baseview::WindowOpenOptions {
@ -10,25 +8,21 @@ fn main() {
parent: baseview::Parent::None, parent: baseview::Parent::None,
}; };
let (_app_message_tx, app_message_rx) = mpsc::channel::<()>(); let _handle = Window::open::<MyProgram>(window_open_options);
// Send _app_message_tx to a separate thread, then send messages to the GUI thread.
let _ = baseview::Window::<MyProgram>::open(window_open_options, app_message_rx);
} }
struct MyProgram {} struct MyProgram {}
impl baseview::AppWindow for MyProgram { impl WindowHandler for MyProgram {
type AppMessage = (); type Message = ();
fn build(_window_handle: baseview::RawWindow, window_info: &baseview::WindowInfo) -> Self { fn build(window: &mut Window) -> Self {
println!("Window info: {:?}", window_info);
Self {} Self {}
} }
fn draw(&mut self) {} fn draw(&mut self, window: &mut Window) {}
fn on_event(&mut self, event: Event) { fn on_event(&mut self, window: &mut Window, event: Event) {
match event { match event {
Event::CursorMotion(x, y) => { Event::CursorMotion(x, y) => {
println!("Cursor moved, x: {}, y: {}", x, y); println!("Cursor moved, x: {}, y: {}", x, y);
@ -69,5 +63,5 @@ impl baseview::AppWindow for MyProgram {
} }
} }
fn on_app_message(&mut self, _message: Self::AppMessage) {} fn on_message(&mut self, window: &mut Window, _message: Self::Message) {}
} }

View file

@ -33,24 +33,12 @@ pub struct WindowOpenOptions<'a> {
pub parent: Parent, pub parent: Parent,
} }
pub trait AppWindow { pub trait WindowHandler {
type AppMessage; type Message;
fn build(window_handle: RawWindow, window_info: &WindowInfo) -> Self; fn build(window: &mut Window) -> Self;
fn draw(&mut self); fn draw(&mut self, window: &mut Window);
fn on_event(&mut self, event: Event); fn on_event(&mut self, window: &mut Window, event: Event);
fn on_app_message(&mut self, message: Self::AppMessage); fn on_message(&mut self, window: &mut Window, message: Self::Message);
}
/// A wrapper for a `RawWindowHandle`. Some context creators expect an `&impl HasRawWindowHandle`.
#[derive(Debug, Copy, Clone)]
pub struct RawWindow {
pub raw_window_handle: raw_window_handle::RawWindowHandle,
}
unsafe impl raw_window_handle::HasRawWindowHandle for RawWindow {
fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
self.raw_window_handle
}
} }

View file

@ -1,27 +1,24 @@
use std::ffi::c_void; use std::ffi::c_void;
use std::sync::mpsc;
use cocoa::appkit::{ use cocoa::appkit::{
NSApp, NSApplication, NSApplicationActivateIgnoringOtherApps, NSApp, NSApplication, NSApplicationActivateIgnoringOtherApps,
NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSRunningApplication, NSView, NSApplicationActivationPolicyRegular, NSBackingStoreBuffered, NSRunningApplication, NSView,
NSWindow, NSWindowStyleMask, NSWindow, NSWindowStyleMask,
}; };
use cocoa::base::{nil, NO}; use cocoa::base::{id, nil, NO};
use cocoa::foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize, NSString}; use cocoa::foundation::{NSAutoreleasePool, NSPoint, NSRect, NSSize, NSString};
use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle}; use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle};
use crate::{ use crate::{MouseScroll, WindowHandler, WindowOpenOptions};
AppWindow, Event, MouseButtonID, MouseScroll, RawWindow, WindowInfo, WindowOpenOptions,
};
pub struct Window<A: AppWindow> { pub struct Window {
app_window: A, ns_window: id,
app_message_rx: mpsc::Receiver<A::AppMessage>, ns_view: id,
} }
impl<A: AppWindow> Window<A> { impl Window {
pub fn open(options: WindowOpenOptions, app_message_rx: mpsc::Receiver<A::AppMessage>) -> Self { pub fn open<H: WindowHandler>(options: WindowOpenOptions) -> WindowHandle {
unsafe { unsafe {
let _pool = NSAutoreleasePool::new(nil); let _pool = NSAutoreleasePool::new(nil);
@ -33,7 +30,7 @@ impl<A: AppWindow> Window<A> {
NSSize::new(options.width as f64, options.height as f64), NSSize::new(options.width as f64, options.height as f64),
); );
let window = NSWindow::alloc(nil) let ns_window = NSWindow::alloc(nil)
.initWithContentRect_styleMask_backing_defer_( .initWithContentRect_styleMask_backing_defer_(
rect, rect,
NSWindowStyleMask::NSTitledWindowMask, NSWindowStyleMask::NSTitledWindowMask,
@ -41,37 +38,34 @@ impl<A: AppWindow> Window<A> {
NO, NO,
) )
.autorelease(); .autorelease();
window.center(); ns_window.center();
window.setTitle_(NSString::alloc(nil).init_str(options.title)); ns_window.setTitle_(NSString::alloc(nil).init_str(options.title));
window.makeKeyAndOrderFront_(nil); ns_window.makeKeyAndOrderFront_(nil);
let view = NSView::alloc(nil).init(); let ns_view = NSView::alloc(nil).init();
window.setContentView_(view); ns_window.setContentView_(ns_view);
let raw_window = RawWindow { let mut window = Window { ns_window, ns_view };
raw_window_handle: RawWindowHandle::MacOS(MacOSHandle {
ns_window: window as *mut c_void,
ns_view: app as *mut c_void,
..raw_window_handle::macos::MacOSHandle::empty()
}),
};
let window_info = WindowInfo { let handler = H::build(&mut window);
width: options.width as u32,
height: options.height as u32,
scale: 1.0,
};
let app_window = A::build(raw_window, &window_info);
let current_app = NSRunningApplication::currentApplication(nil); let current_app = NSRunningApplication::currentApplication(nil);
current_app.activateWithOptions_(NSApplicationActivateIgnoringOtherApps); current_app.activateWithOptions_(NSApplicationActivateIgnoringOtherApps);
app.run(); app.run();
Window { WindowHandle
app_window,
app_message_rx,
}
} }
} }
} }
unsafe impl HasRawWindowHandle for Window {
fn raw_window_handle(&self) -> RawWindowHandle {
RawWindowHandle::MacOS(MacOSHandle {
ns_window: self.ns_window as *mut c_void,
ns_view: self.ns_view as *mut c_void,
..MacOSHandle::empty()
})
}
}
pub struct WindowHandle;

View file

@ -6,18 +6,19 @@ use winapi::um::winuser::{
AdjustWindowRectEx, CreateWindowExA, DefWindowProcA, DestroyWindow, DispatchMessageA, AdjustWindowRectEx, CreateWindowExA, DefWindowProcA, DestroyWindow, DispatchMessageA,
GetMessageA, GetWindowLongPtrA, MessageBoxA, PostMessageA, RegisterClassA, SetTimer, GetMessageA, GetWindowLongPtrA, MessageBoxA, PostMessageA, RegisterClassA, SetTimer,
SetWindowLongPtrA, TranslateMessage, UnregisterClassA, CS_OWNDC, GWLP_USERDATA, MB_ICONERROR, SetWindowLongPtrA, TranslateMessage, UnregisterClassA, CS_OWNDC, GWLP_USERDATA, MB_ICONERROR,
MB_OK, MB_TOPMOST, MSG, WM_CREATE, WM_MOUSEMOVE, WM_PAINT, WM_SHOWWINDOW, WM_TIMER, WNDCLASSA, MB_OK, MB_TOPMOST, MSG, WM_CLOSE, WM_CREATE, WM_MOUSEMOVE, WM_PAINT, WM_SHOWWINDOW, WM_TIMER,
WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX, WS_POPUPWINDOW, WNDCLASSA, WS_CAPTION, WS_CHILD, WS_CLIPSIBLINGS, WS_MAXIMIZEBOX, WS_MINIMIZEBOX,
WS_SIZEBOX, WS_VISIBLE, WS_POPUPWINDOW, WS_SIZEBOX, WS_VISIBLE,
}; };
use std::cell::RefCell;
use std::ffi::c_void; use std::ffi::c_void;
use std::ptr::null_mut; use std::ptr::null_mut;
use std::sync::mpsc;
use std::rc::Rc; use std::rc::Rc;
use std::cell::RefCell;
use crate::{AppWindow, Event, Parent::WithParent, RawWindow, WindowInfo, WindowOpenOptions}; use raw_window_handle::{windows::WindowsHandle, HasRawWindowHandle, RawWindowHandle};
use crate::{Event, Parent::WithParent, WindowHandler, WindowInfo, WindowOpenOptions};
unsafe fn message_box(title: &str, msg: &str) { unsafe fn message_box(title: &str, msg: &str) {
let title = (title.to_owned() + "\0").as_ptr() as *const i8; let title = (title.to_owned() + "\0").as_ptr() as *const i8;
@ -46,14 +47,14 @@ unsafe fn generate_guid() -> String {
const WIN_FRAME_TIMER: usize = 4242; const WIN_FRAME_TIMER: usize = 4242;
unsafe fn handle_timer<A: AppWindow>(win: &RefCell<Window<A>>, timer_id: usize) { unsafe fn handle_timer<H: WindowHandler>(window_state: &RefCell<WindowState<H>>, timer_id: usize) {
match timer_id { match timer_id {
WIN_FRAME_TIMER => {} WIN_FRAME_TIMER => {}
_ => (), _ => (),
} }
} }
unsafe extern "system" fn wnd_proc<A: AppWindow>( unsafe extern "system" fn wnd_proc<H: WindowHandler>(
hwnd: HWND, hwnd: HWND,
msg: UINT, msg: UINT,
wparam: WPARAM, wparam: WPARAM,
@ -66,22 +67,33 @@ unsafe extern "system" fn wnd_proc<A: AppWindow>(
let win_ptr = GetWindowLongPtrA(hwnd, GWLP_USERDATA) as *const c_void; let win_ptr = GetWindowLongPtrA(hwnd, GWLP_USERDATA) as *const c_void;
if !win_ptr.is_null() { if !win_ptr.is_null() {
let win = &*(win_ptr as *const RefCell<Window<A>>); let window_state = &*(win_ptr as *const RefCell<WindowState<H>>);
let mut window = Window { hwnd };
match msg { match msg {
WM_MOUSEMOVE => { WM_MOUSEMOVE => {
let x = (lparam & 0xFFFF) as i32; let x = (lparam & 0xFFFF) as i32;
let y = ((lparam >> 16) & 0xFFFF) as i32; let y = ((lparam >> 16) & 0xFFFF) as i32;
win.borrow_mut().handle_mouse_motion(x, y); window_state
.borrow_mut()
.handler
.on_event(&mut window, Event::CursorMotion(x, y));
return 0; return 0;
} }
WM_TIMER => { WM_TIMER => {
handle_timer(&win, wparam); handle_timer(&window_state, wparam);
return 0; return 0;
} }
WM_PAINT => { WM_PAINT => {
return 0; return 0;
} }
WM_CLOSE => {
window_state
.borrow_mut()
.handler
.on_event(&mut window, Event::WillClose);
return DefWindowProcA(hwnd, msg, wparam, lparam);
}
_ => {} _ => {}
} }
} }
@ -89,13 +101,13 @@ unsafe extern "system" fn wnd_proc<A: AppWindow>(
return DefWindowProcA(hwnd, msg, wparam, lparam); return DefWindowProcA(hwnd, msg, wparam, lparam);
} }
unsafe fn register_wnd_class<A: AppWindow>() -> ATOM { unsafe fn register_wnd_class<H: WindowHandler>() -> ATOM {
// We generate a unique name for the new window class to prevent name collisions // We generate a unique name for the new window class to prevent name collisions
let class_name = format!("Baseview-{}", generate_guid()).as_ptr() as *const i8; let class_name = format!("Baseview-{}", generate_guid()).as_ptr() as *const i8;
let wnd_class = WNDCLASSA { let wnd_class = WNDCLASSA {
style: CS_OWNDC, style: CS_OWNDC,
lpfnWndProc: Some(wnd_proc::<A>), lpfnWndProc: Some(wnd_proc::<H>),
hInstance: null_mut(), hInstance: null_mut(),
lpszClassName: class_name, lpszClassName: class_name,
cbClsExtra: 0, cbClsExtra: 0,
@ -113,20 +125,22 @@ unsafe fn unregister_wnd_class(wnd_class: ATOM) {
UnregisterClassA(wnd_class as _, null_mut()); UnregisterClassA(wnd_class as _, null_mut());
} }
pub struct Window<A: AppWindow> { struct WindowState<H> {
pub(crate) hwnd: HWND,
window_class: ATOM, window_class: ATOM,
app_window: A,
app_message_rx: mpsc::Receiver<A::AppMessage>,
scaling: Option<f64>, // DPI scale, 96.0 is "default". scaling: Option<f64>, // DPI scale, 96.0 is "default".
handler: H,
} }
impl<A: AppWindow> Window<A> { pub struct Window {
pub fn open(options: WindowOpenOptions, app_message_rx: mpsc::Receiver<A::AppMessage>) { hwnd: HWND,
}
impl Window {
pub fn open<H: WindowHandler>(options: WindowOpenOptions) -> WindowHandle {
unsafe { unsafe {
let title = (options.title.to_owned() + "\0").as_ptr() as *const i8; let title = (options.title.to_owned() + "\0").as_ptr() as *const i8;
let window_class = register_wnd_class::<A>(); let window_class = register_wnd_class::<H>();
// todo: manage error ^ // todo: manage error ^
let mut flags = WS_POPUPWINDOW let mut flags = WS_POPUPWINDOW
@ -170,28 +184,15 @@ impl<A: AppWindow> Window<A> {
); );
// todo: manage error ^ // todo: manage error ^
let mut windows_handle = raw_window_handle::windows::WindowsHandle::empty(); let mut window = Window { hwnd };
windows_handle.hwnd = hwnd as *mut std::ffi::c_void;
let raw_window = RawWindow { let handler = H::build(&mut window);
raw_window_handle: raw_window_handle::RawWindowHandle::Windows(windows_handle),
};
let window_info = WindowInfo { let window_state = Rc::new(RefCell::new(WindowState {
width: options.width as u32,
height: options.height as u32,
scale: 1.0,
};
let app_window = A::build(raw_window, &window_info);
let window = Window {
hwnd,
window_class, window_class,
app_window,
app_message_rx,
scaling: None, scaling: None,
}; handler,
}));
let win = Rc::new(RefCell::new(window)); let win = Rc::new(RefCell::new(window));
@ -212,19 +213,18 @@ impl<A: AppWindow> Window<A> {
} }
} }
} }
}
pub fn close(&mut self) { WindowHandle
self.app_window.on_event(Event::WillClose);
// todo: see https://github.com/wrl/rutabaga/blob/f30ff67e157375cafdbafe5fb549f1790443a3a8/src/platform/win/window.c#L402
unsafe {
DestroyWindow(self.hwnd);
unregister_wnd_class(self.window_class);
}
}
pub(crate) fn handle_mouse_motion(&mut self, x: i32, y: i32) {
self.app_window.on_event(Event::CursorMotion(x, y));
} }
} }
unsafe impl HasRawWindowHandle for Window {
fn raw_window_handle(&self) -> RawWindowHandle {
RawWindowHandle::Windows(WindowsHandle {
hwnd: self.hwnd as *mut std::ffi::c_void,
..WindowsHandle::empty()
})
}
}
pub struct WindowHandle;

View file

@ -1,23 +1,19 @@
use std::ffi::CStr; use std::ffi::CStr;
use std::os::raw::{c_ulong, c_void}; use std::os::raw::{c_ulong, c_void};
use std::sync::mpsc;
use super::XcbConnection; use super::XcbConnection;
use crate::{ use crate::{Event, MouseButtonID, MouseScroll, Parent, WindowHandler, WindowOpenOptions};
AppWindow, Event, MouseButtonID, MouseScroll, Parent, RawWindow, WindowInfo, WindowOpenOptions,
};
use raw_window_handle::RawWindowHandle; use raw_window_handle::{unix::XlibHandle, HasRawWindowHandle, RawWindowHandle};
pub struct Window<A: AppWindow> { pub struct Window {
scaling: f64,
xcb_connection: XcbConnection, xcb_connection: XcbConnection,
app_window: A, window_id: u32,
app_message_rx: mpsc::Receiver<A::AppMessage>, scaling: f64,
} }
impl<A: AppWindow> Window<A> { impl Window {
pub fn open(options: WindowOpenOptions, app_message_rx: mpsc::Receiver<A::AppMessage>) -> Self { pub fn open<H: WindowHandler>(options: WindowOpenOptions) -> WindowHandle {
// Convert the parent to a X11 window ID if we're given one // Convert the parent to a X11 window ID if we're given one
let parent = match options.parent { let parent = match options.parent {
Parent::None => None, Parent::None => None,
@ -93,131 +89,130 @@ impl<A: AppWindow> Window<A> {
xcb_connection.conn.flush(); xcb_connection.conn.flush();
let raw_handle = RawWindowHandle::Xlib(raw_window_handle::unix::XlibHandle {
window: window_id as c_ulong,
display: xcb_connection.conn.get_raw_dpy() as *mut c_void,
..raw_window_handle::unix::XlibHandle::empty()
});
let raw_window = RawWindow {
raw_window_handle: raw_handle,
};
let scaling = get_scaling_xft(&xcb_connection) let scaling = get_scaling_xft(&xcb_connection)
.or(get_scaling_screen_dimensions(&xcb_connection)) .or(get_scaling_screen_dimensions(&xcb_connection))
.unwrap_or(1.0); .unwrap_or(1.0);
let window_info = WindowInfo { let mut window = Self {
width: options.width as u32,
height: options.height as u32,
scale: scaling,
};
let app_window = A::build(raw_window, &window_info);
let mut x11_window = Self {
scaling,
xcb_connection, xcb_connection,
app_window, window_id,
app_message_rx, scaling,
}; };
x11_window.run_event_loop(); let mut handler = H::build(&mut window);
x11_window run_event_loop(&mut window, &mut handler);
WindowHandle
} }
}
// Event loop unsafe impl HasRawWindowHandle for Window {
fn run_event_loop(&mut self) { fn raw_window_handle(&self) -> RawWindowHandle {
loop { RawWindowHandle::Xlib(XlibHandle {
// somehow poll self.app_message_rx for messages at the same time window: self.window_id as c_ulong,
display: self.xcb_connection.conn.get_raw_dpy() as *mut c_void,
..raw_window_handle::unix::XlibHandle::empty()
})
}
}
let ev = self.xcb_connection.conn.wait_for_event(); pub struct WindowHandle;
if let Some(event) = ev {
let event_type = event.response_type() & !0x80;
// For all of the keyboard and mouse events, you can fetch // Event loop
// `x`, `y`, `detail`, and `state`. fn run_event_loop<H: WindowHandler>(window: &mut Window, handler: &mut H) {
// - `x` and `y` are the position inside the window where the cursor currently is loop {
// when the event happened. let ev = window.xcb_connection.conn.wait_for_event();
// - `detail` will tell you which keycode was pressed/released (for keyboard events) if let Some(event) = ev {
// or which mouse button was pressed/released (for mouse events). let event_type = event.response_type() & !0x80;
// For mouse events, here's what the value means (at least on my current mouse):
// 1 = left mouse button
// 2 = middle mouse button (scroll wheel)
// 3 = right mouse button
// 4 = scroll wheel up
// 5 = scroll wheel down
// 8 = lower side button ("back" button)
// 9 = upper side button ("forward" button)
// Note that you *will* get a "button released" event for even the scroll wheel
// events, which you can probably ignore.
// - `state` will tell you the state of the main three mouse buttons and some of
// the keyboard modifier keys at the time of the event.
// http://rtbo.github.io/rust-xcb/src/xcb/ffi/xproto.rs.html#445
match event_type { // For all of the keyboard and mouse events, you can fetch
xcb::EXPOSE => { // `x`, `y`, `detail`, and `state`.
self.app_window.draw(); // - `x` and `y` are the position inside the window where the cursor currently is
// when the event happened.
// - `detail` will tell you which keycode was pressed/released (for keyboard events)
// or which mouse button was pressed/released (for mouse events).
// For mouse events, here's what the value means (at least on my current mouse):
// 1 = left mouse button
// 2 = middle mouse button (scroll wheel)
// 3 = right mouse button
// 4 = scroll wheel up
// 5 = scroll wheel down
// 8 = lower side button ("back" button)
// 9 = upper side button ("forward" button)
// Note that you *will* get a "button released" event for even the scroll wheel
// events, which you can probably ignore.
// - `state` will tell you the state of the main three mouse buttons and some of
// the keyboard modifier keys at the time of the event.
// http://rtbo.github.io/rust-xcb/src/xcb/ffi/xproto.rs.html#445
match event_type {
xcb::EXPOSE => {
handler.draw(window);
}
xcb::MOTION_NOTIFY => {
let event = unsafe { xcb::cast_event::<xcb::MotionNotifyEvent>(&event) };
let detail = event.detail();
if detail != 4 && detail != 5 {
handler.on_event(
window,
Event::CursorMotion(event.event_x() as i32, event.event_y() as i32),
);
} }
xcb::MOTION_NOTIFY => { }
let event = unsafe { xcb::cast_event::<xcb::MotionNotifyEvent>(&event) }; xcb::BUTTON_PRESS => {
let detail = event.detail(); let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
let detail = event.detail();
if detail != 4 && detail != 5 { match detail {
self.app_window.on_event(Event::CursorMotion( 4 => {
event.event_x() as i32, handler.on_event(
event.event_y() as i32, window,
)); Event::MouseScroll(MouseScroll {
}
}
xcb::BUTTON_PRESS => {
let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
let detail = event.detail();
match detail {
4 => {
self.app_window.on_event(Event::MouseScroll(MouseScroll {
x_delta: 0.0, x_delta: 0.0,
y_delta: 1.0, y_delta: 1.0,
})); }),
} );
5 => { }
self.app_window.on_event(Event::MouseScroll(MouseScroll { 5 => {
handler.on_event(
window,
Event::MouseScroll(MouseScroll {
x_delta: 0.0, x_delta: 0.0,
y_delta: -1.0, y_delta: -1.0,
})); }),
} );
detail => {
let button_id = mouse_id(detail);
self.app_window.on_event(Event::MouseDown(button_id));
}
} }
} detail => {
xcb::BUTTON_RELEASE => {
let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
let detail = event.detail();
if detail != 4 && detail != 5 {
let button_id = mouse_id(detail); let button_id = mouse_id(detail);
self.app_window.on_event(Event::MouseUp(button_id)); handler.on_event(window, Event::MouseDown(button_id));
} }
} }
xcb::KEY_PRESS => { }
let event = unsafe { xcb::cast_event::<xcb::KeyPressEvent>(&event) }; xcb::BUTTON_RELEASE => {
let detail = event.detail(); let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
let detail = event.detail();
self.app_window.on_event(Event::KeyDown(detail)); if detail != 4 && detail != 5 {
let button_id = mouse_id(detail);
handler.on_event(window, Event::MouseUp(button_id));
} }
xcb::KEY_RELEASE => { }
let event = unsafe { xcb::cast_event::<xcb::KeyReleaseEvent>(&event) }; xcb::KEY_PRESS => {
let detail = event.detail(); let event = unsafe { xcb::cast_event::<xcb::KeyPressEvent>(&event) };
let detail = event.detail();
self.app_window.on_event(Event::KeyUp(detail)); handler.on_event(window, Event::KeyDown(detail));
} }
_ => { xcb::KEY_RELEASE => {
println!("Unhandled event type: {:?}", event_type); let event = unsafe { xcb::cast_event::<xcb::KeyReleaseEvent>(&event) };
} let detail = event.detail();
handler.on_event(window, Event::KeyUp(detail));
}
_ => {
println!("Unhandled event type: {:?}", event_type);
} }
} }
} }