From 33c1cc80b681ba081f46994bc35cfe8b28d300f2 Mon Sep 17 00:00:00 2001 From: missing <4a656666official@gmail.com> Date: Sun, 3 Jul 2022 11:24:24 -0700 Subject: [PATCH] make destroying focused client actually work --- src/x11/client.rs | 104 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 95 insertions(+), 9 deletions(-) diff --git a/src/x11/client.rs b/src/x11/client.rs index 583c5b5..c1a4c74 100644 --- a/src/x11/client.rs +++ b/src/x11/client.rs @@ -13,16 +13,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use breadx::auto::xproto::{InputFocus, SetInputFocusRequest}; -use breadx::prelude::{AsyncDisplayXprotoExt, SetMode}; use breadx::{ - AsyncDisplay, AsyncDisplayExt, BreadError, ConfigureWindowParameters, ErrorCode, EventMask, - Window, WindowParameters, XidType, + 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 slotmap::{new_key_type, SlotMap}; -use std::collections::HashMap; -use std::future::Future; -use std::pin::Pin; +use std::{collections::HashMap, future::Future, pin::Pin, slice}; use crate::{Result, XcrabError, CONFIG}; @@ -584,7 +583,7 @@ impl XcrabWindowManager { self.remove_client(conn, focused).await?; - frame.win.free_async(conn).await?; + frame.kill_client(conn).await?; Ok(()) } else { @@ -703,6 +702,8 @@ impl FramedWindow { self.frame.unmap_async(conn).await?; + may_not_exist(self.win.unmap_async(conn).await)?; + may_not_exist(self.win.reparent_async(conn, root, 0, 0).await)?; // no longer related to us, remove from save set may_not_exist(self.win.change_save_set_async(conn, SetMode::Delete).await)?; @@ -711,6 +712,91 @@ impl FramedWindow { Ok(()) } + + async fn kill_client(self, conn: &mut Dpy) -> Result<()> { + struct ListOfAtom(Vec); + + impl AsByteSequence for ListOfAtom { + fn size(&self) -> usize { + unimplemented!() + } + fn as_bytes(&self, _: &mut [u8]) -> usize { + unimplemented!() + } + + fn from_bytes(mut bytes: &[u8]) -> Option<(Self, usize)> { + let mut index = 0; + let mut vec = Vec::new(); + + while let Some((atom, index2)) = Atom::from_bytes(bytes) { + vec.push(atom); + index += index2; + bytes = &bytes[index2..]; + } + + Some((Self(vec), index)) + } + } + + fn as_u8_slice(data: &[u32]) -> &[u8] { + // SAFETY: i believe in you to see that this is sound + unsafe { + slice::from_raw_parts( + data.as_ptr().cast::(), + data.len().checked_mul(4).unwrap(), + ) + } + } + + let wm_protocols = conn + .intern_atom_immediate_async("WM_PROTOCOLS", true) + .await?; + assert!(wm_protocols.xid != 0); + let wm_delete_window = conn + .intern_atom_immediate_async("WM_DELETE_WINDOW", true) + .await?; + assert!(wm_delete_window.xid != 0); + + let prop = self + .win + .get_property_immediate_async::<_, ListOfAtom>( + conn, + wm_protocols, + PropertyType::Atom, + false, + ) + .await?; + let protocols = prop.unwrap().0; // should never fail to parse + + if protocols.contains(&wm_delete_window) { + let data = [wm_delete_window.xid, 0, 0, 0, 0]; + let data_bytes = as_u8_slice(&data); + + conn.send_event_async( + self.win, + EventMask::default(), + Event::ClientMessage(ClientMessageEvent { + event_type: 33, // constant, check x protocol docs + format: 32, // tell the x server to byte-flip as if the data was a [u32] + sequence: 0, // this should be filled in for us by the x server + window: self.win, + ty: wm_protocols, + data: ClientMessageData::from_bytes(data_bytes).unwrap().0, // why the field is private is beyond me + }), + ) + .await?; + + // tokio::spawn(async { + // tokio::time::sleep(Duration::from_secs(3)).await; + + // // TODO: if the client isnt responding, `free_async` the window (maybe show a popup?) + // }); + } else { + self.win.free_async(conn).await?; + } + + Ok(()) + } } async fn frame(conn: &mut Dpy, win: Window) -> Result { @@ -722,7 +808,7 @@ async fn frame(conn: &mut Dpy, win: Window) -> Resul let frame = conn .create_simple_window_async( root, - // theoretically, all of these could be ignoring since they are set later + // theoretically, all of these could be ignored since they are set later geometry.x, geometry.y, geometry.width,