From db17c9dbfeeef38638133d0175faa0b2d5831868 Mon Sep 17 00:00:00 2001 From: Janis Date: Wed, 24 Nov 2021 22:57:17 +0100 Subject: [PATCH] more work on XLib backend --- src/backends/mod.rs | 2 +- src/backends/xlib/keysym.rs | 28 +++-- src/backends/xlib/mod.rs | 245 ++++++++++++++++++++++++++++++------ src/state.rs | 2 +- 4 files changed, 231 insertions(+), 46 deletions(-) diff --git a/src/backends/mod.rs b/src/backends/mod.rs index bbbd489..ca6b653 100644 --- a/src/backends/mod.rs +++ b/src/backends/mod.rs @@ -9,7 +9,7 @@ pub trait WindowServerBackend { fn new() -> Self; - fn next_event(&self) -> window_event::WindowEvent; + fn next_event(&mut self) -> window_event::WindowEvent; fn add_keybind(&mut self, keybind: KeyBind, window: Option); fn remove_keybind( &mut self, diff --git a/src/backends/xlib/keysym.rs b/src/backends/xlib/keysym.rs index ec025b4..4351dba 100644 --- a/src/backends/xlib/keysym.rs +++ b/src/backends/xlib/keysym.rs @@ -38,21 +38,31 @@ pub fn mouse_button_to_xbutton(button: MouseButton) -> i32 { } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] -pub struct XKeysym(u32); +pub struct XKeySym(pub u32); -impl Borrow for XKeysym { +impl XKeySym { + pub fn new(value: u32) -> Self { + Self(value) + } + + pub fn get(&self) -> u32 { + self.0 + } +} + +impl Borrow for XKeySym { fn borrow(&self) -> &u32 { &self.0 } } -impl AsRef for XKeysym { +impl AsRef for XKeySym { fn as_ref(&self) -> &u32 { &self.0 } } -impl Deref for XKeysym { +impl Deref for XKeySym { type Target = u32; fn deref(&self) -> &Self::Target { @@ -60,12 +70,12 @@ impl Deref for XKeysym { } } -impl From for VirtualKeyCode { - fn from(value: XKeysym) -> Self { +impl From for VirtualKeyCode { + fn from(value: XKeySym) -> Self { keysym_to_virtual_keycode(*value).unwrap() } } -impl From for XKeysym { +impl From for XKeySym { fn from(value: VirtualKeyCode) -> Self { Self(virtual_keycode_to_keysym(value).unwrap()) } @@ -2092,12 +2102,12 @@ mod tests { #[test] fn test_keysym_to_vkc() { - let keysym: XKeysym = VirtualKeyCode::W.into(); + let keysym: XKeySym = VirtualKeyCode::W.into(); let keycode: VirtualKeyCode = keysym.into(); assert_eq!(keycode, VirtualKeyCode::W); - let keysym2: XKeysym = keycode.into(); + let keysym2: XKeySym = keycode.into(); assert_eq!(keysym2, keysym); assert_eq!(&x11::keysym::XK_W, keysym.as_ref()); diff --git a/src/backends/xlib/mod.rs b/src/backends/xlib/mod.rs index 030aac2..4d0b1ba 100644 --- a/src/backends/xlib/mod.rs +++ b/src/backends/xlib/mod.rs @@ -8,11 +8,16 @@ use std::{ use thiserror::Error; -use x11::xlib::{self, Atom, Window, XEvent, XInternAtom}; +use x11::xlib::{ + self, Atom, KeyPress, KeyRelease, Window, XEvent, XInternAtom, XKeyEvent, +}; -use self::keysym::xev_to_mouse_button; +use crate::backends::window_event::ModifierKey; + +use self::keysym::{virtual_keycode_to_keysym, xev_to_mouse_button, XKeySym}; use super::{ + keycodes::VirtualKeyCode, window_event::{ ButtonEvent, ConfigureEvent, DestroyEvent, EnterEvent, KeyState, MapEvent, ModifierState, UnmapEvent, WindowEvent, @@ -131,26 +136,154 @@ impl Display { pub struct XLib { display: Display, + modifier_state: ModifierState, root: Window, screen: i32, atoms: XLibAtoms, keybinds: Vec<()>, } +impl Drop for XLib { + fn drop(&mut self) { + self.close_dpy(); + } +} + impl XLib { fn dpy(&self) -> *mut xlib::Display { self.display.get() } - fn next_xevent(&self) -> XEvent { + fn close_dpy(&self) { unsafe { - let mut event = - std::mem::MaybeUninit::::zeroed().assume_init(); - xlib::XNextEvent(self.dpy(), &mut event); - - event + xlib::XCloseDisplay(self.dpy()); } } + + fn next_xevent(&mut self) -> XEvent { + let event = unsafe { + let mut event = std::mem::MaybeUninit::::zeroed(); + xlib::XNextEvent(self.dpy(), event.as_mut_ptr()); + + event.assume_init() + }; + + match event.get_type() { + xlib::KeyPress | xlib::KeyRelease => { + self.update_modifier_state(AsRef::::as_ref( + &event, + )); + } + _ => {} + } + + event + } + + fn check_for_protocol( + &self, + window: xlib::Window, + proto: xlib::Atom, + ) -> bool { + let mut protos: *mut xlib::Atom = std::ptr::null_mut(); + let mut num_protos: i32 = 0; + + unsafe { + if xlib::XGetWMProtocols( + self.dpy(), + window, + &mut protos, + &mut num_protos, + ) != 0 + { + for i in 0..num_protos { + if *protos.offset(i as isize) == proto { + return true; + } + } + } + } + + return false; + } + + fn send_protocol(&self, window: xlib::Window, proto: Atom) -> bool { + if self.check_for_protocol(window, proto) { + let mut data = xlib::ClientMessageData::default(); + data.set_long(0, proto as i64); + + let mut event = XEvent { + client_message: xlib::XClientMessageEvent { + type_: xlib::ClientMessage, + serial: 0, + display: self.dpy(), + send_event: 0, + window, + format: 32, + message_type: self.atoms.wm_protocols, + data, + }, + }; + + unsafe { + xlib::XSendEvent( + self.dpy(), + window, + 0, + xlib::NoEventMask, + &mut event, + ); + } + + true + } else { + false + } + } + + #[allow(non_upper_case_globals)] + fn update_modifier_state(&mut self, keyevent: &XKeyEvent) { + //keyevent.keycode + let keysym = self.keyev_to_keysym(keyevent); + + use x11::keysym::*; + + let modifier = match keysym.get() { + XK_Shift_L | XK_Shift_R => Some(ModifierKey::Shift), + XK_Control_L | XK_Control_R => Some(ModifierKey::Control), + XK_Alt_L | XK_Alt_R => Some(ModifierKey::Alt), + XK_ISO_Level3_Shift => Some(ModifierKey::AltGr), + XK_Caps_Lock => Some(ModifierKey::ShiftLock), + XK_Num_Lock => Some(ModifierKey::NumLock), + XK_Win_L | XK_Win_R => Some(ModifierKey::Super), + XK_Super_L | XK_Super_R => Some(ModifierKey::Super), + _ => None, + }; + + if let Some(modifier) = modifier { + match keyevent.type_ { + KeyPress => self.modifier_state.set_mod(modifier), + KeyRelease => self.modifier_state.unset_mod(modifier), + _ => unreachable!("keyyevent != (KeyPress | KeyRelease)"), + } + } + } + + fn vk_to_keycode(&self, vk: VirtualKeyCode) -> i32 { + unsafe { + xlib::XKeysymToKeycode( + self.dpy(), + virtual_keycode_to_keysym(vk).unwrap() as u64, + ) as i32 + } + } + + fn keyev_to_keysym(&self, ev: &XKeyEvent) -> XKeySym { + let keysym = + unsafe { xlib::XLookupKeysym(ev as *const _ as *mut _, 0) }; + + XKeySym::new(keysym as u32) + } } impl TryFrom for XLibWindowEvent { @@ -191,7 +324,7 @@ impl TryFrom for XLibWindowEvent { KeyState::Released }; - let modifierstate = ModifierState::new(); + let modifierstate = ModifierState::empty(); Ok(Self::ButtonEvent(ButtonEvent::new( ev.window, @@ -208,8 +341,12 @@ impl TryFrom for XLibWindowEvent { impl WindowServerBackend for XLib { type Window = xlib::Window; - fn next_event(&self) -> super::window_event::WindowEvent { - self.next_xevent().try_into().unwrap() + fn next_event(&mut self) -> super::window_event::WindowEvent { + std::iter::from_fn(|| { + TryInto::::try_into(self.next_xevent()).ok() + }) + .next() + .unwrap() } fn add_keybind( @@ -265,10 +402,24 @@ impl WindowServerBackend for XLib { } fn screen_size(&self) -> (i32, i32) { - todo!() + unsafe { + let mut wa = + std::mem::MaybeUninit::::zeroed(); + + xlib::XGetWindowAttributes(self.dpy(), self.root, wa.as_mut_ptr()); + + let wa = wa.assume_init(); + + (wa.width, wa.height) + } } fn kill_window(&self, window: Self::Window) { + if !self.send_protocol(window, self.atoms.wm_delete_window) { + unsafe { + xlib::XKillClient(self.dpy(), window); + } + } todo!() } @@ -290,33 +441,57 @@ impl WindowServerBackend for XLib { } struct XLibAtoms { - protocols: Atom, - delete_window: Atom, - active_window: Atom, - take_focus: Atom, + wm_protocols: Atom, + wm_delete_window: Atom, + wm_active_window: Atom, + wm_take_focus: Atom, + net_supported: Atom, + net_active_window: Atom, + net_client_list: Atom, + net_wm_name: Atom, + net_wm_state: Atom, + net_wm_state_fullscreen: Atom, + net_wm_window_type: Atom, + net_wm_window_type_dialog: Atom, } impl XLibAtoms { fn init(display: Display) -> Self { - unsafe { - Self { - protocols: { - let name = CString::new("WM_PROTOCOLS").unwrap(); - XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) - }, - delete_window: { - let name = CString::new("WM_DELETE_WINDOW").unwrap(); - XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) - }, - active_window: { - let name = CString::new("WM_ACTIVE_WINDOW").unwrap(); - XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) - }, - take_focus: { - let name = CString::new("WM_TAKE_FOCUS").unwrap(); - XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) - }, - } + Self { + wm_protocols: Self::get_atom(&display, "WM_PROTOCOLS").unwrap(), + wm_delete_window: Self::get_atom(&display, "WM_DELETE_WINDOW") + .unwrap(), + wm_active_window: Self::get_atom(&display, "WM_ACTIVE_WINDOW") + .unwrap(), + wm_take_focus: Self::get_atom(&display, "WM_TAKE_FOCUS").unwrap(), + net_supported: Self::get_atom(&display, "_NET_SUPPORTED").unwrap(), + net_active_window: Self::get_atom(&display, "_NET_ACTIVE_WINDOW") + .unwrap(), + net_client_list: Self::get_atom(&display, "_NET_CLIENT_LIST") + .unwrap(), + net_wm_name: Self::get_atom(&display, "_NET_WM_NAME").unwrap(), + net_wm_state: Self::get_atom(&display, "_NET_WM_STATE").unwrap(), + net_wm_state_fullscreen: Self::get_atom( + &display, + "_NET_WM_STATE_FULLSCREEN", + ) + .unwrap(), + net_wm_window_type: Self::get_atom(&display, "_NET_WM_WINDOW_TYPE") + .unwrap(), + net_wm_window_type_dialog: Self::get_atom( + &display, + "_NET_WM_WINDOW_TYPE_DIALOG", + ) + .unwrap(), + } + } + + fn get_atom(display: &Display, atom: &str) -> Option { + let name = CString::new(atom).ok()?; + match unsafe { XInternAtom(display.get(), name.as_c_str().as_ptr(), 0) } + { + 0 => None, + atom => Some(atom), } } } diff --git a/src/state.rs b/src/state.rs index dac5a53..e278b64 100644 --- a/src/state.rs +++ b/src/state.rs @@ -382,7 +382,7 @@ where fn kill_client(&mut self) { if let Some(client) = self.clients.get_focused().into_option() { - //self.xlib.kill_client(client); + self.backend.kill_window(client.window); } }