Fix issues on LeftWM
This commit is contained in:
parent
996c482535
commit
65e405232d
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "xdi"
|
name = "xdi"
|
||||||
version = "1.0.0"
|
version = "1.0.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
description = "X11 Desktop Information"
|
description = "X11 Desktop Information"
|
||||||
|
|
||||||
|
|||||||
133
src/main.rs
133
src/main.rs
@ -38,11 +38,16 @@ xcb::atoms_struct! {
|
|||||||
net_number_of_desktops => b"_NET_NUMBER_OF_DESKTOPS",
|
net_number_of_desktops => b"_NET_NUMBER_OF_DESKTOPS",
|
||||||
net_current_desktop => b"_NET_CURRENT_DESKTOP",
|
net_current_desktop => b"_NET_CURRENT_DESKTOP",
|
||||||
net_client_list => b"_NET_CLIENT_LIST",
|
net_client_list => b"_NET_CLIENT_LIST",
|
||||||
|
net_client_list_stacking => b"_NET_CLIENT_LIST_STACKING",
|
||||||
net_wm_desktop => b"_NET_WM_DESKTOP",
|
net_wm_desktop => b"_NET_WM_DESKTOP",
|
||||||
net_active_window => b"_NET_ACTIVE_WINDOW",
|
net_active_window => b"_NET_ACTIVE_WINDOW",
|
||||||
net_wm_name => b"_NET_WM_NAME",
|
net_wm_name => b"_NET_WM_NAME",
|
||||||
utf8_string => b"UTF8_STRING",
|
utf8_string => b"UTF8_STRING",
|
||||||
wm_class => b"WM_CLASS",
|
wm_class => b"WM_CLASS",
|
||||||
|
wm_state => b"WM_STATE",
|
||||||
|
net_wm_state => b"_NET_WM_STATE",
|
||||||
|
net_wm_state_above => b"_NET_WM_STATE_ABOVE",
|
||||||
|
net_wm_state_sticky => b"_NET_WM_STATE_STICKY",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +92,11 @@ impl X11 {
|
|||||||
.first()
|
.first()
|
||||||
.copied();
|
.copied();
|
||||||
|
|
||||||
|
// Try to get stacking order to determine active window
|
||||||
|
let stacking_ids = self
|
||||||
|
.property_u32(self.root, self.atoms.net_client_list_stacking)
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
let client_ids = self.property_u32(self.root, self.atoms.net_client_list)?;
|
let client_ids = self.property_u32(self.root, self.atoms.net_client_list)?;
|
||||||
|
|
||||||
let mut workspaces: Vec<Workspace> = (0..workspace_count)
|
let mut workspaces: Vec<Workspace> = (0..workspace_count)
|
||||||
@ -98,14 +108,18 @@ impl X11 {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut active_window = None;
|
let mut active_window = None;
|
||||||
|
let mut windows_on_current = Vec::new();
|
||||||
|
|
||||||
for id in client_ids {
|
for id in client_ids {
|
||||||
let window = self.window_info(id)?;
|
// Try to get window info, skip if it fails (window might be invalid)
|
||||||
|
let Ok(window) = self.window_info(id) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Try to get desktop property, skip if it fails
|
||||||
let desktop = self
|
let desktop = self
|
||||||
.property_u32(x::Window::new(id), self.atoms.net_wm_desktop)?
|
.property_u32_opt(x::Window::new(id), self.atoms.net_wm_desktop)?
|
||||||
.first()
|
.and_then(|v| v.first().copied());
|
||||||
.copied();
|
|
||||||
|
|
||||||
let Some(desktop) = desktop else {
|
let Some(desktop) = desktop else {
|
||||||
continue;
|
continue;
|
||||||
@ -121,6 +135,10 @@ impl X11 {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if desktop == current {
|
||||||
|
windows_on_current.push(id);
|
||||||
|
}
|
||||||
|
|
||||||
if active_id == Some(id) {
|
if active_id == Some(id) {
|
||||||
active_window = Some(window.clone());
|
active_window = Some(window.clone());
|
||||||
}
|
}
|
||||||
@ -128,12 +146,50 @@ impl X11 {
|
|||||||
workspace.windows.push(window);
|
workspace.windows.push(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep the active window available even if its desktop could
|
// If _NET_ACTIVE_WINDOW is 0 or not set, try to determine active window
|
||||||
// not be resolved.
|
// from the stacking order on the current workspace
|
||||||
if active_window.is_none()
|
if active_window.is_none() || active_id == Some(0) {
|
||||||
&& let Some(id) = active_id
|
// Find the topmost window on the current workspace
|
||||||
{
|
let mut current_workspace_windows: Vec<_> = windows_on_current
|
||||||
active_window = Some(self.window_info(id)?);
|
.iter()
|
||||||
|
.filter(|id| {
|
||||||
|
// Check if this window is on the current workspace
|
||||||
|
self.property_u32_opt(x::Window::new(**id), self.atoms.net_wm_desktop)
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.and_then(|v| v.first().copied())
|
||||||
|
== Some(current)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Sort by stacking order (if available)
|
||||||
|
if !stacking_ids.is_empty() {
|
||||||
|
current_workspace_windows.sort_by_key(|id| {
|
||||||
|
stacking_ids
|
||||||
|
.iter()
|
||||||
|
.position(|&stack_id| stack_id == **id)
|
||||||
|
.unwrap_or(usize::MAX)
|
||||||
|
});
|
||||||
|
// The last one in stacking order is on top
|
||||||
|
if let Some(&top_id) = current_workspace_windows.last()
|
||||||
|
&& let Ok(info) = self.window_info(*top_id)
|
||||||
|
{
|
||||||
|
active_window = Some(info);
|
||||||
|
}
|
||||||
|
} else if let Some(&top_id) = current_workspace_windows.last() {
|
||||||
|
// If no stacking info, just use the last found
|
||||||
|
if let Ok(info) = self.window_info(*top_id) {
|
||||||
|
active_window = Some(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we still don't have an active window, try the old method
|
||||||
|
if active_window.is_none()
|
||||||
|
&& let Some(id) = active_id
|
||||||
|
&& id != 0
|
||||||
|
{
|
||||||
|
active_window = self.window_info(id).ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(State {
|
Ok(State {
|
||||||
@ -146,11 +202,11 @@ impl X11 {
|
|||||||
let window = x::Window::new(id);
|
let window = x::Window::new(id);
|
||||||
|
|
||||||
let name = self
|
let name = self
|
||||||
.property_bytes(window, self.atoms.net_wm_name, self.atoms.utf8_string)?
|
.property_bytes_opt(window, self.atoms.net_wm_name, self.atoms.utf8_string)?
|
||||||
.and_then(|bytes| String::from_utf8(bytes).ok());
|
.and_then(|bytes| String::from_utf8(bytes).ok());
|
||||||
|
|
||||||
let class = self
|
let class = self
|
||||||
.property_bytes(window, self.atoms.wm_class, x::ATOM_STRING)?
|
.property_bytes_opt(window, self.atoms.wm_class, x::ATOM_STRING)?
|
||||||
.map(|bytes| parse_wm_class(&bytes));
|
.map(|bytes| parse_wm_class(&bytes));
|
||||||
|
|
||||||
Ok(WindowInfo { id, name, class })
|
Ok(WindowInfo { id, name, class })
|
||||||
@ -175,7 +231,33 @@ impl X11 {
|
|||||||
Ok(reply.value::<u32>().to_vec())
|
Ok(reply.value::<u32>().to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn property_bytes(
|
fn property_u32_opt(
|
||||||
|
&self,
|
||||||
|
window: x::Window,
|
||||||
|
property: x::Atom,
|
||||||
|
) -> Result<Option<Vec<u32>>, Box<dyn Error>> {
|
||||||
|
let cookie = self.conn.send_request(&x::GetProperty {
|
||||||
|
delete: false,
|
||||||
|
window,
|
||||||
|
property,
|
||||||
|
r#type: x::ATOM_ANY,
|
||||||
|
long_offset: 0,
|
||||||
|
long_length: u32::MAX,
|
||||||
|
});
|
||||||
|
|
||||||
|
let Ok(reply) = self.conn.wait_for_reply(cookie) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
let value = reply.value::<u32>();
|
||||||
|
if value.is_empty() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(value.to_vec()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn property_bytes_opt(
|
||||||
&self,
|
&self,
|
||||||
window: x::Window,
|
window: x::Window,
|
||||||
property: x::Atom,
|
property: x::Atom,
|
||||||
@ -190,7 +272,9 @@ impl X11 {
|
|||||||
long_length: u32::MAX,
|
long_length: u32::MAX,
|
||||||
});
|
});
|
||||||
|
|
||||||
let reply = self.conn.wait_for_reply(cookie)?;
|
let Ok(reply) = self.conn.wait_for_reply(cookie) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
let value = reply.value::<u8>();
|
let value = reply.value::<u8>();
|
||||||
|
|
||||||
@ -205,12 +289,13 @@ impl X11 {
|
|||||||
self.select_events(
|
self.select_events(
|
||||||
self.root,
|
self.root,
|
||||||
x::EventMask::PROPERTY_CHANGE | x::EventMask::SUBSTRUCTURE_NOTIFY,
|
x::EventMask::PROPERTY_CHANGE | x::EventMask::SUBSTRUCTURE_NOTIFY,
|
||||||
)?;
|
);
|
||||||
|
|
||||||
let client_ids = self.property_u32(self.root, self.atoms.net_client_list)?;
|
let client_ids = self.property_u32(self.root, self.atoms.net_client_list)?;
|
||||||
|
|
||||||
for id in client_ids {
|
for id in client_ids {
|
||||||
self.select_events(x::Window::new(id), x::EventMask::PROPERTY_CHANGE)?;
|
// Ignore errors when selecting events on windows that might be gone
|
||||||
|
let () = self.select_events(x::Window::new(id), x::EventMask::PROPERTY_CHANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.conn.flush()?;
|
self.conn.flush()?;
|
||||||
@ -218,19 +303,14 @@ impl X11 {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_events(
|
fn select_events(&self, window: x::Window, event_mask: x::EventMask) {
|
||||||
&self,
|
|
||||||
window: x::Window,
|
|
||||||
event_mask: x::EventMask,
|
|
||||||
) -> Result<(), Box<dyn Error>> {
|
|
||||||
let cookie = self.conn.send_request_checked(&x::ChangeWindowAttributes {
|
let cookie = self.conn.send_request_checked(&x::ChangeWindowAttributes {
|
||||||
window,
|
window,
|
||||||
value_list: &[x::Cw::EventMask(event_mask)],
|
value_list: &[x::Cw::EventMask(event_mask)],
|
||||||
});
|
});
|
||||||
|
|
||||||
self.conn.check_request(cookie)?;
|
// Ignore errors for windows that might be gone
|
||||||
|
let _ = self.conn.check_request(cookie);
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_for_relevant_event(&self) -> Result<(), Box<dyn Error>> {
|
fn wait_for_relevant_event(&self) -> Result<(), Box<dyn Error>> {
|
||||||
@ -245,10 +325,8 @@ impl X11 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
xcb::Event::X(x::Event::CreateNotify(event)) => {
|
xcb::Event::X(x::Event::CreateNotify(event)) => {
|
||||||
let _ = self.select_events(event.window(), x::EventMask::PROPERTY_CHANGE);
|
let () = self.select_events(event.window(), x::EventMask::PROPERTY_CHANGE);
|
||||||
|
|
||||||
self.conn.flush()?;
|
self.conn.flush()?;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,6 +345,7 @@ impl X11 {
|
|||||||
atom == self.atoms.net_number_of_desktops
|
atom == self.atoms.net_number_of_desktops
|
||||||
|| atom == self.atoms.net_current_desktop
|
|| atom == self.atoms.net_current_desktop
|
||||||
|| atom == self.atoms.net_client_list
|
|| atom == self.atoms.net_client_list
|
||||||
|
|| atom == self.atoms.net_client_list_stacking
|
||||||
|| atom == self.atoms.net_wm_desktop
|
|| atom == self.atoms.net_wm_desktop
|
||||||
|| atom == self.atoms.net_active_window
|
|| atom == self.atoms.net_active_window
|
||||||
|| atom == self.atoms.net_wm_name
|
|| atom == self.atoms.net_wm_name
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user