diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 176a458..0000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -* text=auto diff --git a/.gitignore b/.gitignore index ea8c4bf..40d9aca 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/.idea \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 035c264..27ff78e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ dependencies = [ [[package]] name = "async-task" -version = "4.2.0" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" +checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "atomic-waker" @@ -99,15 +99,15 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.9.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc" +checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835" [[package]] name = "bytes" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" [[package]] name = "cache-padded" @@ -129,9 +129,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "concurrent-queue" -version = "1.2.2" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" dependencies = [ "cache-padded", ] @@ -144,15 +144,15 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" [[package]] name = "event-listener" -version = "2.5.2" +version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "fastrand" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] @@ -290,9 +290,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] name = "parking" @@ -344,9 +344,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" dependencies = [ "unicode-ident", ] @@ -362,9 +362,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] @@ -377,18 +377,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.137" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" dependencies = [ "proc-macro2", "quote", @@ -406,9 +406,12 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] [[package]] name = "slotmap" @@ -421,9 +424,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "socket2" @@ -463,10 +466,11 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.19.2" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" dependencies = [ + "autocfg", "bytes", "libc", "memchr", @@ -503,9 +507,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" [[package]] name = "version_check" diff --git a/README.md b/README.md index bc69e36..190a64e 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,4 @@ A window manager written in [Rust](https://rust-lang.org). ## Contributing -Please contribute, we don't know what the fuck we are doing. How we even got to this point is beyond us. +Please contribute, we don't know what the fuck we are doing. How we even got to this point is beyond us. \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 1a338d6..1874b07 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ use std::fmt::{Debug, Display}; use breadx::{ + auto::xproto::{GrabKeyRequest, GrabMode, KeyButMask, Keycode, Keysym, ModMask}, keyboard::KeyboardState, prelude::{AsyncDisplay, AsyncDisplayXprotoExt, MapState}, traits::DisplayBase, @@ -27,7 +28,7 @@ use breadx::{ use lazy_static::lazy_static; -use tokio::sync::mpsc; +use tokio::sync::mpsc::unbounded_channel; mod config; mod msg_listener; @@ -35,6 +36,8 @@ mod x11; use x11::client::{may_not_exist, XcrabWindowManager}; +use std::collections::HashMap; + #[non_exhaustive] pub enum XcrabError { Bread(BreadError), @@ -76,7 +79,6 @@ impl From for XcrabError { } lazy_static! { - // pub static ref CONFIG: config::XcrabConfig = config::load_file().unwrap_or_default(); pub static ref CONFIG: config::XcrabConfig = config::load_file().unwrap_or_else(|e| { println!("[CONFIG] Error parsing config: {e}"); println!("[CONFIG] Falling back to default config"); @@ -91,9 +93,9 @@ impl Display for XcrabError { Self::Io(ie) => Display::fmt(ie, f)?, Self::Toml(te) => Display::fmt(te, f)?, Self::Var(ve) => Display::fmt(ve, f)?, - Self::Custom(fe) => Display::fmt(fe, f)?, Self::ClientDoesntExist => Display::fmt("client didn't exist", f)?, - } + Self::Custom(fe) => Display::fmt(fe, f)?, + }; Ok(()) } @@ -117,7 +119,7 @@ async fn main() -> Result<()> { // listen for substructure redirects to intercept events like window creation root.set_event_mask_async( &mut conn, - EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY, + EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY | EventMask::KEY_PRESS, ) .await?; @@ -137,8 +139,68 @@ async fn main() -> Result<()> { conn.ungrab_server_async().await?; - let (send, mut recv) = mpsc::unbounded_channel(); - let (result_send, result_recv) = mpsc::unbounded_channel(); + let mut mask = ModMask::new(false, false, true, false, false, false, false, false, false); + let mut keyboard_state = KeyboardState::new_async(&mut conn).await?; + let keymap = x11::client::keymap(&mut keyboard_state); + let mut request_key = *keymap.get(&120).ok_or_else(|| { + XcrabError::Custom("At least one letter could not be found in the keymap".to_string()) + })?; + + for &binds in CONFIG.binds.keys() { + for keysym in 97..122_u32 { + let keycode = keymap.get(&keysym).ok_or_else(|| { + XcrabError::Custom( + "At least one letter could not be found in the keymap".to_string(), + ) + })?; + let iter_char = keyboard_state + .process_keycode(*keycode, KeyButMask::default()) + .ok_or_else(|| { + XcrabError::Custom( + "The keycode returned from the keymap could not be processed".to_string(), + ) + })? + .as_char() + .ok_or_else(|| { + XcrabError::Custom("The processed Key could not be cast as a char".to_string()) + })?; + if iter_char == binds.key { + request_key = *keycode; + mask.inner = binds.mods.inner; + } + } + } + + mask.set_Two(true); + + conn.exchange_request_async(GrabKeyRequest { + req_type: 33, + owner_events: false, + length: 4, + grab_window: root, + modifiers: mask, + key: request_key, + pointer_mode: GrabMode::Async, + keyboard_mode: GrabMode::Async, + }) + .await?; + + mask.set_Two(false); + + conn.exchange_request_async(GrabKeyRequest { + req_type: 33, + owner_events: false, + length: 4, + grab_window: root, + modifiers: mask, + key: request_key, + pointer_mode: GrabMode::Async, + keyboard_mode: GrabMode::Async, + }) + .await?; + + let (send, mut recv) = unbounded_channel(); + let (result_send, result_recv) = unbounded_channel(); tokio::spawn(msg_listener::listener_task( CONFIG.msg.clone().unwrap_or_default().socket_path, @@ -146,25 +208,18 @@ async fn main() -> Result<()> { result_recv, )); - let mut keyboard_state = KeyboardState::new_async(&mut conn).await?; - loop { // biased mode makes select! poll the channel first in order to keep xcrab-msg from being // starved by x11 events. Probably unnecessary, but better safe than sorry. tokio::select! { biased; Some(s) = recv.recv() => msg_listener::on_recv(s, &mut manager, &mut conn, &result_send).await?, - Ok(ev) = conn.wait_for_event_async() => process_event(ev, - &mut manager, - &mut conn, - root, - &mut keyboard_state, - ).await?, + Ok(ev) = conn.wait_for_event_async() => process_event(ev, &mut manager, &mut conn, root, &mut keyboard_state).await?, } } } -#[allow(clippy::too_many_lines)] // FIXME: missing help i have no idea how to make this shorter +#[allow(clippy::too_many_lines)] async fn process_event( ev: Event, manager: &mut XcrabWindowManager, @@ -172,20 +227,6 @@ async fn process_event( root: Window, keyboard_state: &mut KeyboardState, ) -> Result<()> { - let focused = { - // A new scope is required in order to drop the future stored here, and therefore free the - // immutable borrow of `manager` - let orig = manager - .get_focused() - .await - .map(|w| (w, manager.get_framed_window(w))); - if let Some((w, fut)) = orig { - Some((w, fut.await)) - } else { - None - } - }; - match ev { Event::MapRequest(ev) => { manager.add_client(conn, ev.window).await?; @@ -221,77 +262,22 @@ async fn process_event( manager.remove_client(conn, ev.window).await?; } } - Event::ButtonPress(mut ev) => { - if ev.detail == 1 && manager.has_client(ev.event) { + Event::ButtonPress(ev) => { + if ev.detail == 1 { manager.set_focus(conn, ev.event).await?; } - if let Some((focused, focused_frame)) = focused { - if ev.event == focused_frame.input { - ev.event = focused; - conn.send_event_async(focused, EventMask::BUTTON_PRESS, Event::ButtonPress(ev)) - .await?; - } - } } - Event::KeyPress(mut ev) => { + Event::KeyPress(ev) => { if let Some(k) = keyboard_state.process_keycode(ev.detail, ev.state) { if let Some(c) = k.as_char() { for (&bind, action) in &CONFIG.binds { - if bind.key == c && bind.mods == ev.state { + if bind.key == c { action.eval(manager, conn).await?; } } } } - - // keybind did not match, forward instead - if let Some((focused, _)) = focused { - ev.event = focused; - conn.send_event_async(focused, EventMask::KEY_PRESS, Event::KeyPress(ev)) - .await?; - } } - Event::KeyRelease(mut ev) => { - if let Some((focused, focused_frame)) = focused { - if ev.event == focused_frame.input { - ev.event = focused; - conn.send_event_async(focused, EventMask::KEY_RELEASE, Event::KeyRelease(ev)) - .await?; - } - } - } - Event::ButtonRelease(mut ev) => { - if let Some((focused, focused_frame)) = focused { - if ev.event == focused_frame.input { - ev.event = focused; - conn.send_event_async( - focused, - EventMask::BUTTON_RELEASE, - Event::ButtonRelease(ev), - ) - .await?; - } - } - } - Event::EnterNotify(mut ev) => { - if let Some((focused, focused_frame)) = focused { - if ev.event == focused_frame.input { - ev.event = focused; - conn.send_event_async(focused, EventMask::ENTER_WINDOW, Event::EnterNotify(ev)) - .await?; - } - } - } - Event::LeaveNotify(mut ev) => { - if let Some((focused, focused_frame)) = focused { - if ev.event == focused_frame.input { - ev.event = focused; - conn.send_event_async(focused, EventMask::LEAVE_WINDOW, Event::LeaveNotify(ev)) - .await?; - } - } - } - _ => {} } Ok(()) diff --git a/src/msg/config.rs b/src/msg/config.rs index d5a0485..d42c834 100644 --- a/src/msg/config.rs +++ b/src/msg/config.rs @@ -12,7 +12,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . - use crate::Result; use serde::Deserialize; use std::path::PathBuf; @@ -40,11 +39,8 @@ impl Default for XcrabMsgConfig { fn load_file_inner() -> Result { let home_dir = get_home(); - let contents = std::fs::read_to_string(format!("{}/.config/xcrab/config.toml", home_dir))?; - let config: XcrabConfig = toml::from_str(&contents)?; - Ok(config) } diff --git a/src/msg/main.rs b/src/msg/main.rs index 3d24557..1cc8dd0 100644 --- a/src/msg/main.rs +++ b/src/msg/main.rs @@ -49,6 +49,7 @@ async fn main() -> Result<()> { let path = conf.msg.socket_path; let stream = UnixStream::connect(path).await?; + let (mut read, mut write) = stream.into_split(); write.write_all(msg.as_bytes()).await?; @@ -58,7 +59,6 @@ async fn main() -> Result<()> { let mut buf = String::new(); read.read_to_string(&mut buf).await?; - if !buf.is_empty() { return Err(CustomError(buf).into()); } diff --git a/src/x11/client.rs b/src/x11/client.rs index 7d937ad..b1bb66a 100644 --- a/src/x11/client.rs +++ b/src/x11/client.rs @@ -13,15 +13,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use breadx::{ - auto::xproto::{ClientMessageEvent, InputFocus, SetInputFocusRequest}, - client_message_data::ClientMessageData, - prelude::{AsByteSequence, AsyncDisplayXprotoExt, PropertyType, SetMode}, - AsyncDisplay, AsyncDisplayExt, Atom, BreadError, ConfigureWindowParameters, ErrorCode, Event, - EventMask, Window, WindowParameters, XidType, -}; +use breadx::{auto::xproto::{ClientMessageEvent, InputFocus, SetInputFocusRequest}, client_message_data::ClientMessageData, prelude::{AsByteSequence, AsyncDisplayXprotoExt, PropertyType, SetMode}, AsyncDisplay, AsyncDisplayExt, Atom, BreadError, ConfigureWindowParameters, ErrorCode, Event, EventMask, Window, WindowParameters, XidType, KeyboardState}; use slotmap::{new_key_type, SlotMap}; use std::{collections::HashMap, future::Future, pin::Pin, slice}; +use breadx::auto::xproto::{KeyButMask, Keycode, Keysym}; use crate::{Result, XcrabError, CONFIG}; @@ -242,10 +237,7 @@ impl XcrabWindowManager { }; if let Some(focus) = self.focused { - let focused_key = self.clients.get(&focus).unwrap(); - let focused = self.rects.get(*focused_key).unwrap(); - let focused_frame = focused.unwrap_client().frame; - req.focus = focused_frame.input; + req.focus = focus; } conn.exchange_request_async(req).await?; @@ -640,7 +632,6 @@ pub fn may_not_exist(res: breadx::Result) -> breadx::Result { pub struct FramedWindow { pub frame: Window, pub win: Window, - pub input: Window, } impl FramedWindow { @@ -700,27 +691,12 @@ impl FramedWindow { .await, )?; - self.input - .configure_async( - conn, - ConfigureWindowParameters { - x: Some(0), - y: Some(0), - width, - height, - border_width: None, - ..Default::default() - }, - ) - .await?; - Ok(()) } async fn map(self, conn: &mut Dpy) -> Result<()> { may_not_exist(self.win.map_async(conn).await)?; self.frame.map_async(conn).await?; - self.input.map_async(conn).await?; Ok(()) } @@ -736,10 +712,6 @@ impl FramedWindow { // no longer related to us, remove from save set may_not_exist(self.win.change_save_set_async(conn, SetMode::Delete).await)?; - self.input.unmap_async(conn).await?; - - self.input.free_async(conn).await?; - self.frame.free_async(conn).await?; Ok(()) @@ -851,21 +823,6 @@ async fn frame(conn: &mut Dpy, win: Window) -> Resul ) .await?; - let input = conn - .create_window_async( - frame, - breadx::WindowClass::InputOnly, - None, - Some(conn.default_visual_id()), - 0, - 0, - geometry.width, - geometry.height, - 0, - WindowParameters::default(), - ) - .await?; - frame .set_event_mask_async( conn, @@ -876,21 +833,23 @@ async fn frame(conn: &mut Dpy, win: Window) -> Resul win.set_event_mask_async(conn, EventMask::BUTTON_PRESS) .await?; - input - .set_event_mask_async( - conn, - EventMask::BUTTON_PRESS - | EventMask::BUTTON_RELEASE - | EventMask::KEY_PRESS - | EventMask::KEY_RELEASE - | EventMask::ENTER_WINDOW - | EventMask::LEAVE_WINDOW, - ) - .await?; - may_not_exist(win.change_save_set_async(conn, SetMode::Insert).await)?; may_not_exist(win.reparent_async(conn, frame, 0, 0).await)?; - Ok(FramedWindow { frame, win, input }) + Ok(FramedWindow { frame, win }) } + +pub fn keymap(state: &mut KeyboardState) -> HashMap { + let mut map: HashMap = HashMap::new(); + for keycode in 8..255_u8 { + let key = state.process_keycode(keycode, KeyButMask::default()); + let keysyms = state.lookup_keysyms(keycode); + if key != None { + for keysym in keysyms { + map.insert(*keysym, keycode); + } + } + } + map +} \ No newline at end of file