From 98459d620c28867241fec6362ab7a998720ba6d3 Mon Sep 17 00:00:00 2001 From: NoOneBtw Date: Thu, 13 May 2021 23:38:42 +0200 Subject: [PATCH] started with a backend using `x11rb` --- Cargo.toml | 3 + src/backends/mod.rs | 4 + src/backends/xcb.rs | 363 +++++++++++++++++++++++++++++++++++++++++++ src/backends/xlib.rs | 34 ++++ src/main.rs | 4 + 5 files changed, 408 insertions(+) create mode 100644 src/backends/mod.rs create mode 100644 src/backends/xcb.rs create mode 100644 src/backends/xlib.rs diff --git a/Cargo.toml b/Cargo.toml index 3215225..d14dbcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,9 @@ edition = "2018" [dependencies] x11 = {version = "2.18.2", features = ["xlib"] } +x11rb = "0.8.1" +num-derive = "0.3.3" +num-traits = "0.2.14" log = "0.4.13" simple_logger = "1.11.0" dirs = "3.0.2" diff --git a/src/backends/mod.rs b/src/backends/mod.rs new file mode 100644 index 0000000..6d3aee4 --- /dev/null +++ b/src/backends/mod.rs @@ -0,0 +1,4 @@ +mod xcb; +mod xlib; + +pub trait WindowServerBackend {} diff --git a/src/backends/xcb.rs b/src/backends/xcb.rs new file mode 100644 index 0000000..1ed2c90 --- /dev/null +++ b/src/backends/xcb.rs @@ -0,0 +1,363 @@ +//x11 backend +#![allow(dead_code)] + +use log::error; +use num_traits::FromPrimitive; +use num_traits::ToPrimitive; +use std::sync::Arc; + +use x11rb::{ + connect, + connection::Connection, + errors::ReplyError, + errors::ReplyOrIdError, + protocol::xproto::{ + Atom, ChangeWindowAttributesAux, ConnectionExt, EventMask, Screen, + Setup, + }, +}; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn keysyms() { + let xcb = create_backend().unwrap(); + + let mapping = xcb + .connection + .get_keyboard_mapping( + xcb.setup().min_keycode, + xcb.setup().max_keycode - xcb.setup().min_keycode + 1, + ) + .unwrap(); + + let mapping = mapping.reply().unwrap(); + + for (i, keysyms) in mapping + .keysyms + .chunks(mapping.keysyms_per_keycode as usize) + .enumerate() + { + println!( + "keycode: {:#x?}\tkeysyms: {:0x?}", + xcb.setup().min_keycode as usize + i, + keysyms + ); + } + } +} + +#[repr(u8)] +#[derive(FromPrimitive, ToPrimitive)] +pub enum MouseButton { + Left = 1, + Middle, + Right, + ScrollUp, + ScrollDown, + ScrollLeft, + ScrollRight, + Backwards, + Forwards, +} + +#[derive(FromPrimitive, ToPrimitive)] +pub enum Key { + BackSpace = 0xff08, + Tab = 0xff09, + Linefeed = 0xff0a, + Clear = 0xff0b, + Return = 0xff0d, + Pause = 0xff13, + ScrollLock = 0xff14, + SysReq = 0xff15, + Escape = 0xff1b, + Delete = 0xffff, + Home = 0xff50, + Left = 0xff51, + Up = 0xff52, + Right = 0xff53, + Down = 0xff54, + PageUp = 0xff55, + PageDown = 0xff56, + End = 0xff57, + Begin = 0xff58, + Space = 0x0020, + Exclam = 0x0021, + Quotedbl = 0x0022, + Numbersign = 0x0023, + Dollar = 0x0024, + Percent = 0x0025, + Ampersand = 0x0026, + Apostrophe = 0x0027, + ParenLeft = 0x0028, + ParenRight = 0x0029, + Asterisk = 0x002a, + Plus = 0x002b, + Comma = 0x002c, + Minus = 0x002d, + Period = 0x002e, + Slash = 0x002f, + Zero = 0x0030, + One = 0x0031, + Two = 0x0032, + Three = 0x0033, + Four = 0x0034, + Five = 0x0035, + Six = 0x0036, + Seven = 0x0037, + Eight = 0x0038, + Nine = 0x0039, + Colon = 0x003a, + Semicolon = 0x003b, + Less = 0x003c, + Equal = 0x003d, + Greater = 0x003e, + Question = 0x003f, + At = 0x0040, + UppercaseA = 0x0041, + UppercaseB = 0x0042, + UppercaseC = 0x0043, + UppercaseD = 0x0044, + UppercaseE = 0x0045, + UppercaseF = 0x0046, + UppercaseG = 0x0047, + UppercaseH = 0x0048, + UppercaseI = 0x0049, + UppercaseJ = 0x004a, + UppercaseK = 0x004b, + UppercaseL = 0x004c, + UppercaseM = 0x004d, + UppercaseN = 0x004e, + UppercaseO = 0x004f, + UppercaseP = 0x0050, + UppercaseQ = 0x0051, + UppercaseR = 0x0052, + UppercaseS = 0x0053, + UppercaseT = 0x0054, + UppercaseU = 0x0055, + UppercaseV = 0x0056, + UppercaseW = 0x0057, + UppercaseX = 0x0058, + UppercaseY = 0x0059, + UppercaseZ = 0x005a, + BracketLeft = 0x005b, + Backslash = 0x005c, + BracketRight = 0x005d, + AsciiCircum = 0x005e, + Underscore = 0x005f, + Grave = 0x0060, + LowercaseA = 0x0061, + LowercaseB = 0x0062, + LowercaseC = 0x0063, + LowercaseD = 0x0064, + LowercaseE = 0x0065, + LowercaseF = 0x0066, + LowercaseG = 0x0067, + LowercaseH = 0x0068, + LowercaseI = 0x0069, + LowercaseJ = 0x006a, + LowercaseK = 0x006b, + LowercaseL = 0x006c, + LowercaseM = 0x006d, + LowercaseN = 0x006e, + LowercaseO = 0x006f, + LowercaseP = 0x0070, + LowercaseQ = 0x0071, + LowercaseR = 0x0072, + LowercaseS = 0x0073, + LowercaseT = 0x0074, + LowercaseU = 0x0075, + LowercaseV = 0x0076, + LowercaseW = 0x0077, + LowercaseX = 0x0078, + LowercaseY = 0x0079, + LowercaseZ = 0x007a, + BraceLeft = 0x007b, + Bar = 0x007c, + BraceRight = 0x007d, + AsciiTilde = 0x007e, +} + +impl Key {} + +// '<,'>s/.* XK_\([A-z,_]*\)[ ]*\(0x[a-f,0-9]*\).*/\1 = \2,/ + +struct Atoms { + wm_protocols: Atom, + wm_state: Atom, + wm_delete_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 Atoms { + fn new(connection: Arc) -> Result + where + C: Connection, + { + let wm_protocols = connection.intern_atom(false, b"WM_PROTOCOLS")?; + let wm_state = connection.intern_atom(false, b"WM_STATE")?; + let wm_delete_window = + connection.intern_atom(false, b"WM_DELETE_WINDOW")?; + let wm_take_focus = connection.intern_atom(false, b"WM_TAKE_FOCUS")?; + let net_supported = connection.intern_atom(false, b"_NET_SUPPORTED")?; + let net_active_window = + connection.intern_atom(false, b"_NET_ACTIVE_WINDOW")?; + let net_client_list = + connection.intern_atom(false, b"_NET_CLIENT_LIST")?; + let net_wm_name = connection.intern_atom(false, b"_NET_WM_NAME")?; + let net_wm_state = connection.intern_atom(false, b"_NET_WM_STATE")?; + let net_wm_state_fullscreen = + connection.intern_atom(false, b"_NET_WM_STATE_FULLSCREEN")?; + let net_wm_window_type = + connection.intern_atom(false, b"_NET_WM_WINDOW_TYPE")?; + let net_wm_window_type_dialog = + connection.intern_atom(false, b"_NET_WM_WINDOW_TYPE_DIALOG")?; + + Ok(Self { + wm_protocols: wm_protocols.reply()?.atom, + wm_state: wm_state.reply()?.atom, + wm_delete_window: wm_delete_window.reply()?.atom, + wm_take_focus: wm_take_focus.reply()?.atom, + net_supported: net_supported.reply()?.atom, + net_active_window: net_active_window.reply()?.atom, + net_client_list: net_client_list.reply()?.atom, + net_wm_name: net_wm_name.reply()?.atom, + net_wm_state: net_wm_state.reply()?.atom, + net_wm_state_fullscreen: net_wm_state_fullscreen.reply()?.atom, + net_wm_window_type: net_wm_window_type.reply()?.atom, + net_wm_window_type_dialog: net_wm_window_type_dialog.reply()?.atom, + }) + } +} + +pub struct X11Backend +where + C: Connection, +{ + connection: Arc, + screen: usize, + atoms: Atoms, +} + +pub fn create_backend( +) -> Result, Box> +{ + let (connection, screen) = connect(None)?; + + Ok(X11Backend::new(Arc::new(connection), screen)?) +} + +impl X11Backend +where + C: Connection, +{ + pub fn new( + connection: Arc, + screen: usize, + ) -> Result { + let atoms = Atoms::new(connection.clone())?; + Ok(Self { + connection, + screen, + atoms, + }) + } + + fn setup(&self) -> &Setup { + self.connection.setup() + } + + fn screen(&self) -> &Screen { + &self.connection.setup().roots[self.screen] + } + + fn root(&self) -> u32 { + self.screen().root + } + + // this needs the mask aswell to determine the keysym + fn keysym_for_keycode(&self, keycode: u8) -> Option { + let setup = self.setup(); + let mapping = self + .connection + .get_keyboard_mapping( + setup.min_keycode, + setup.max_keycode - setup.min_keycode + 1, + ) + .ok()?; + + let mapping = mapping.reply().ok()?; + + mapping + .keysyms + .chunks(mapping.keysyms_per_keycode as usize) + .nth(keycode as usize) + .and_then(|keysyms| Key::from_u32(keysyms[0])) + } + + fn keycode_for_keysym(&self, keysym: &K) -> Option + where + K: num_traits::ToPrimitive, + { + if let Some(keysym) = keysym.to_u32() { + let setup = self.setup(); + let mapping = self + .connection + .get_keyboard_mapping( + setup.min_keycode, + setup.max_keycode - setup.min_keycode + 1, + ) + .ok()?; + + let mapping = mapping.reply().ok()?; + + mapping + .keysyms + .chunks(mapping.keysyms_per_keycode as usize) + .enumerate() + .find_map(|(i, keysyms)| { + if keysyms.contains(&keysym) { + Some(setup.min_keycode + i as u8) + } else { + None + } + }) + } else { + None + } + } + + pub fn request_substructure_events(&self) -> Result<(), ReplyError> { + let attributes = ChangeWindowAttributesAux::default().event_mask( + EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY, + ); + + match self + .connection + .change_window_attributes(self.root(), &attributes)? + .check() + { + Ok(_) => Ok(()), + Err(err) => { + error!( + "Failed to request substructure redirect/notify: another \ + window manager is running. {:#?}", + err + ); + + Err(err) + } + } + } +} diff --git a/src/backends/xlib.rs b/src/backends/xlib.rs new file mode 100644 index 0000000..65b6eb5 --- /dev/null +++ b/src/backends/xlib.rs @@ -0,0 +1,34 @@ +use std::ptr::null; + +use x11::xlib::{Window, XRootWindow}; + +// xlib backend + +pub struct XLib { + display: *mut x11::xlib::Display, + screen: i32, +} + +impl Drop for XLib { + fn drop(&mut self) { + unsafe { + x11::xlib::XCloseDisplay(self.display); + } + } +} + +impl XLib { + pub fn new() -> Self { + let (display, screen) = unsafe { + let display = x11::xlib::XOpenDisplay(null()); + let screen = x11::xlib::XDefaultScreen(display); + + (display, screen) + }; + Self { display, screen } + } + + fn root_window(&self) -> Window { + unsafe { XRootWindow(self.display, self.screen) } + } +} diff --git a/src/main.rs b/src/main.rs index 63d27bf..808ab9f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,10 @@ use log4rs::{ }; use state::WMConfig; +#[macro_use] +extern crate num_derive; + +mod backends; mod client_logic; mod clients; mod clients2;