diff --git a/Cargo.toml b/Cargo.toml index 516adbf..1b0453c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/examples/open_window.rs b/examples/open_window.rs index 2f22de9..107d79a 100644 --- a/examples/open_window.rs +++ b/examples/open_window.rs @@ -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); diff --git a/src/x11/window.rs b/src/x11/window.rs index 1553c34..9950cd2 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -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::(&event) }; + + // what an absolute trajedy this all is + let data = event.data().data; + let (_, data32, _) = unsafe { data.align_to::() }; + + 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::(&event) }; let detail = event.detail(); @@ -201,6 +238,7 @@ impl Window { ); } } + xcb::BUTTON_PRESS => { let event = unsafe { xcb::cast_event::(&event) }; let detail = event.detail(); @@ -230,6 +268,7 @@ impl Window { } } } + xcb::BUTTON_RELEASE => { let event = unsafe { xcb::cast_event::(&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::(&event) }; let detail = event.detail(); handler.on_event(self, Event::KeyDown(detail)); } + xcb::KEY_RELEASE => { let event = unsafe { xcb::cast_event::(&event) }; let detail = event.detail(); handler.on_event(self, Event::KeyUp(detail)); } + _ => { println!("Unhandled event type: {:?}", event_type); } diff --git a/src/x11/xcb_connection.rs b/src/x11/xcb_connection.rs index f4bab15..ec6fd60 100644 --- a/src/x11/xcb_connection.rs +++ b/src/x11/xcb_connection.rs @@ -7,19 +7,54 @@ use std::ffi::{ CStr }; +pub(crate) struct Atoms { + pub wm_protocols: Option, + pub wm_delete_window: Option +} + 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 { - xcb::Connection::connect_with_xlib_display() - .map(|(conn, xlib_display)| - Self { - conn, - xlib_display - }) + 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, + + atoms: Atoms { + wm_protocols, + wm_delete_window + } + }) } // Try to get the scaling with this function first.