x11: Event::WillClose support
this is a nightmare of ICCCM protocols but hey it's done now.
This commit is contained in:
parent
ce48ae111a
commit
b64183fb19
|
@ -17,6 +17,7 @@ raw-window-handle = "0.3.3"
|
|||
[target.'cfg(target_os="linux")'.dependencies]
|
||||
xcb = { version = "0.9", features = ["thread", "xlib_xcb", "dri2"] }
|
||||
x11 = { version = "2.18", features = ["xlib"] }
|
||||
xcb-util = { version = "0.3", features = ["icccm"] }
|
||||
libc = "0.2"
|
||||
nix = "0.18"
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ impl WindowHandler for MyProgram {
|
|||
|
||||
fn on_frame(&mut self) {}
|
||||
|
||||
fn on_event(&mut self, _window: &mut Window, event: Event) {
|
||||
fn on_event(&mut self, window: &mut Window, event: Event) {
|
||||
match event {
|
||||
Event::CursorMotion(x, y) => {
|
||||
println!("Cursor moved, x: {}, y: {}", x, y);
|
||||
|
|
|
@ -89,6 +89,17 @@ impl Window {
|
|||
title.as_bytes(),
|
||||
);
|
||||
|
||||
xcb_connection.atoms.wm_protocols
|
||||
.zip(xcb_connection.atoms.wm_delete_window)
|
||||
.map(|(wm_protocols, wm_delete_window)| {
|
||||
xcb_util::icccm::set_wm_protocols(
|
||||
&xcb_connection.conn,
|
||||
window_id,
|
||||
wm_protocols,
|
||||
&[wm_delete_window]
|
||||
);
|
||||
});
|
||||
|
||||
xcb_connection.conn.flush();
|
||||
|
||||
let scaling = xcb_connection.get_scaling().unwrap_or(1.0);
|
||||
|
@ -187,9 +198,35 @@ impl Window {
|
|||
// http://rtbo.github.io/rust-xcb/src/xcb/ffi/xproto.rs.html#445
|
||||
|
||||
match event_type {
|
||||
////
|
||||
// keys
|
||||
////
|
||||
|
||||
xcb::EXPOSE => {
|
||||
handler.on_frame();
|
||||
}
|
||||
|
||||
xcb::CLIENT_MESSAGE => {
|
||||
let event = unsafe { xcb::cast_event::<xcb::ClientMessageEvent>(&event) };
|
||||
|
||||
// what an absolute trajedy this all is
|
||||
let data = event.data().data;
|
||||
let (_, data32, _) = unsafe { data.align_to::<u32>() };
|
||||
|
||||
let wm_delete_window = self.xcb_connection.atoms.wm_delete_window.unwrap_or(xcb::NONE);
|
||||
|
||||
if wm_delete_window == data32[0] {
|
||||
handler.on_event(self, Event::WillClose);
|
||||
|
||||
// FIXME: handler should decide whether window stays open or not
|
||||
self.event_loop_running = false;
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
// mouse
|
||||
////
|
||||
|
||||
xcb::MOTION_NOTIFY => {
|
||||
let event = unsafe { xcb::cast_event::<xcb::MotionNotifyEvent>(&event) };
|
||||
let detail = event.detail();
|
||||
|
@ -201,6 +238,7 @@ impl Window {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
xcb::BUTTON_PRESS => {
|
||||
let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
|
||||
let detail = event.detail();
|
||||
|
@ -230,6 +268,7 @@ impl Window {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
xcb::BUTTON_RELEASE => {
|
||||
let event = unsafe { xcb::cast_event::<xcb::ButtonPressEvent>(&event) };
|
||||
let detail = event.detail();
|
||||
|
@ -239,18 +278,25 @@ impl Window {
|
|||
handler.on_event(self, Event::MouseUp(button_id));
|
||||
}
|
||||
}
|
||||
|
||||
////
|
||||
// keys
|
||||
////
|
||||
|
||||
xcb::KEY_PRESS => {
|
||||
let event = unsafe { xcb::cast_event::<xcb::KeyPressEvent>(&event) };
|
||||
let detail = event.detail();
|
||||
|
||||
handler.on_event(self, Event::KeyDown(detail));
|
||||
}
|
||||
|
||||
xcb::KEY_RELEASE => {
|
||||
let event = unsafe { xcb::cast_event::<xcb::KeyReleaseEvent>(&event) };
|
||||
let detail = event.detail();
|
||||
|
||||
handler.on_event(self, Event::KeyUp(detail));
|
||||
}
|
||||
|
||||
_ => {
|
||||
println!("Unhandled event type: {:?}", event_type);
|
||||
}
|
||||
|
|
|
@ -7,18 +7,53 @@ use std::ffi::{
|
|||
CStr
|
||||
};
|
||||
|
||||
pub(crate) struct Atoms {
|
||||
pub wm_protocols: Option<u32>,
|
||||
pub wm_delete_window: Option<u32>
|
||||
}
|
||||
|
||||
pub struct XcbConnection {
|
||||
pub conn: xcb::Connection,
|
||||
pub xlib_display: i32,
|
||||
|
||||
pub(crate) atoms: Atoms
|
||||
}
|
||||
|
||||
macro_rules! intern_atoms {
|
||||
($conn:expr, $( $name:ident ),+ ) => {{
|
||||
$(
|
||||
#[allow(non_snake_case)]
|
||||
let $name = xcb::intern_atom($conn, true, stringify!($name));
|
||||
)+
|
||||
|
||||
// splitting request and reply to improve throughput
|
||||
|
||||
(
|
||||
$( $name.get_reply()
|
||||
.map(|r| r.atom())
|
||||
.ok()),+
|
||||
)
|
||||
}};
|
||||
}
|
||||
|
||||
|
||||
impl XcbConnection {
|
||||
pub fn new() -> Result<Self, xcb::base::ConnError> {
|
||||
xcb::Connection::connect_with_xlib_display()
|
||||
.map(|(conn, xlib_display)|
|
||||
Self {
|
||||
let (conn, xlib_display) = xcb::Connection::connect_with_xlib_display()?;
|
||||
|
||||
let (wm_protocols, wm_delete_window) =
|
||||
intern_atoms!(&conn,
|
||||
WM_PROTOCOLS,
|
||||
WM_DELETE_WINDOW);
|
||||
|
||||
Ok(Self {
|
||||
conn,
|
||||
xlib_display
|
||||
xlib_display,
|
||||
|
||||
atoms: Atoms {
|
||||
wm_protocols,
|
||||
wm_delete_window
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue