added support for transient windows
transient windows float but stay on the `VirtualScreen` their parent window is on
This commit is contained in:
parent
cb0d7fbd18
commit
46e2c7448c
|
@ -17,6 +17,7 @@ mod client {
|
|||
pub(crate) window: Window,
|
||||
pub(crate) size: (i32, i32),
|
||||
pub(crate) position: (i32, i32),
|
||||
pub(crate) transient_for: Option<Window>,
|
||||
}
|
||||
|
||||
impl Default for Client {
|
||||
|
@ -25,6 +26,7 @@ mod client {
|
|||
window: 0,
|
||||
size: (100, 100),
|
||||
position: (0, 0),
|
||||
transient_for: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +37,16 @@ mod client {
|
|||
window,
|
||||
size,
|
||||
position,
|
||||
transient_for: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_transient(window: Window, size: (i32, i32), transient: Window) -> Self {
|
||||
Self {
|
||||
window,
|
||||
size,
|
||||
transient_for: Some(transient),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +56,10 @@ mod client {
|
|||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_transient(&self) -> bool {
|
||||
self.transient_for.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Client {
|
||||
|
@ -278,18 +294,32 @@ impl ClientState {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, client: Client) -> Option<&Client> {
|
||||
pub fn insert(&mut self, mut client: Client) -> Option<&Client> {
|
||||
let key = client.key();
|
||||
|
||||
self.clients.insert(key, client);
|
||||
if client.is_transient() && self.contains(&client.transient_for.unwrap()) {
|
||||
let transient = self.get(&client.transient_for.unwrap()).unwrap();
|
||||
|
||||
if let Some(vs) = self.virtual_screens.front_mut() {
|
||||
vs.insert(&key);
|
||||
client.position = {
|
||||
(
|
||||
transient.position.0 + (transient.size.0 - client.size.0) / 2,
|
||||
transient.position.1 + (transient.size.1 - client.size.1) / 2,
|
||||
)
|
||||
};
|
||||
|
||||
self.floating_clients.insert(key, client);
|
||||
} else {
|
||||
self.clients.insert(key, client);
|
||||
|
||||
if let Some(vs) = self.virtual_screens.front_mut() {
|
||||
vs.insert(&key);
|
||||
}
|
||||
}
|
||||
|
||||
self.focus_client(&key);
|
||||
|
||||
self.clients.get(&key)
|
||||
// TODO: eventually make this function return a `ClientEntry` instead of an `Option`.
|
||||
self.get(&key).into_option()
|
||||
}
|
||||
|
||||
pub fn remove<K>(&mut self, key: &K)
|
||||
|
@ -315,20 +345,47 @@ impl ClientState {
|
|||
self.floating_clients.iter()
|
||||
}
|
||||
|
||||
fn iter_all_clients(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
||||
self.floating_clients.iter().chain(self.clients.iter())
|
||||
}
|
||||
|
||||
pub fn iter_hidden(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
||||
self.clients
|
||||
.iter()
|
||||
.filter(move |&(k, _)| !self.virtual_screens.front().unwrap().contains(k))
|
||||
self.iter_all_clients()
|
||||
.filter(move |&(k, _)| !self.is_client_visible(k))
|
||||
}
|
||||
|
||||
pub fn iter_visible(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
||||
self.iter_floating().chain(self.iter_current_screen())
|
||||
self.iter_all_clients()
|
||||
.filter(move |&(k, _)| self.is_client_visible(k))
|
||||
}
|
||||
|
||||
pub fn iter_current_screen(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
||||
self.clients
|
||||
.iter()
|
||||
.filter(move |&(k, _)| self.virtual_screens.front().unwrap().contains(k))
|
||||
.filter(move |&(k, _)| self.current_vs().contains(k))
|
||||
}
|
||||
|
||||
/// Returns reference to the current `VirtualScreen`.
|
||||
fn current_vs(&self) -> &VirtualScreen {
|
||||
// there is always at least one (1) virtual screen.
|
||||
self.virtual_screens.front().unwrap()
|
||||
}
|
||||
|
||||
fn is_client_visible<K>(&self, key: &K) -> bool
|
||||
where
|
||||
K: ClientKey,
|
||||
{
|
||||
match self.get(key) {
|
||||
ClientEntry::Floating(c) => {
|
||||
if let Some(transient_for) = c.transient_for {
|
||||
self.is_client_visible(&transient_for)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
ClientEntry::Tiled(_) => self.current_vs().contains(key),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get<K>(&self, key: &K) -> ClientEntry<&Client>
|
||||
|
@ -407,10 +464,18 @@ impl ClientState {
|
|||
self.floating_clients.insert(key, client);
|
||||
self.remove_from_virtual_screens(&key);
|
||||
}
|
||||
(None, Some(client)) => {
|
||||
self.clients.insert(key, client);
|
||||
if let Some(vs) = self.virtual_screens.front_mut() {
|
||||
vs.insert(&key);
|
||||
(None, Some(floating_client)) => {
|
||||
// transient clients cannot be tiled
|
||||
match floating_client.is_transient() {
|
||||
true => {
|
||||
self.floating_clients.insert(key, floating_client);
|
||||
}
|
||||
false => {
|
||||
self.clients.insert(key, floating_client);
|
||||
if let Some(vs) = self.virtual_screens.front_mut() {
|
||||
vs.insert(&key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
@ -539,12 +604,14 @@ impl ClientState {
|
|||
let width = width / (1 + i32::from(!vs.aux.is_empty()));
|
||||
|
||||
// make sure we dont devide by 0
|
||||
// height is max height / number of clients in the stack
|
||||
let master_height = height
|
||||
/ match NonZeroI32::new(vs.master.len() as i32) {
|
||||
Some(i) => i.get(),
|
||||
None => 1,
|
||||
};
|
||||
|
||||
// height is max height / number of clients in the stack
|
||||
let aux_height = height
|
||||
/ match NonZeroI32::new(vs.aux.len() as i32) {
|
||||
Some(i) => i.get(),
|
||||
|
@ -559,6 +626,7 @@ impl ClientState {
|
|||
// add repeating height for each window and x pos for each window
|
||||
.zip(repeat(master_height).zip(repeat(0i32)))
|
||||
.chain(
|
||||
// same things for aux stack
|
||||
vs.aux
|
||||
.iter()
|
||||
.enumerate()
|
||||
|
|
30
src/state.rs
30
src/state.rs
|
@ -87,12 +87,12 @@ impl WindowManager {
|
|||
|
||||
self.add_keybind(KeyBinding::new(
|
||||
self.xlib.make_key("T", Mod1Mask),
|
||||
|wm, _| wm.spawn("xterm", &[]),
|
||||
|wm, _| wm.spawn("alacritty", &[]),
|
||||
));
|
||||
|
||||
self.add_keybind(KeyBinding::new(
|
||||
self.xlib.make_key("Return", Mod1Mask | ShiftMask),
|
||||
|wm, _| wm.spawn("xterm", &[]),
|
||||
|wm, _| wm.spawn("alacritty", &[]),
|
||||
));
|
||||
|
||||
self.add_keybind(KeyBinding::new(
|
||||
|
@ -321,10 +321,6 @@ impl WindowManager {
|
|||
Direction::Right => self.clients.rotate_right(),
|
||||
}
|
||||
|
||||
self.clients
|
||||
.iter_hidden()
|
||||
.for_each(|(_, c)| self.xlib.hide_client(c));
|
||||
|
||||
self.arrange_clients();
|
||||
|
||||
// focus first client in all visible clients
|
||||
|
@ -335,12 +331,20 @@ impl WindowManager {
|
|||
}
|
||||
}
|
||||
|
||||
fn hide_hidden_clients(&self) {
|
||||
self.clients
|
||||
.iter_hidden()
|
||||
.for_each(|(_, c)| self.xlib.hide_client(c));
|
||||
}
|
||||
|
||||
fn arrange_clients(&mut self) {
|
||||
let (width, height) = self.xlib.dimensions();
|
||||
self.clients.arrange_virtual_screen(width, height, Some(2));
|
||||
|
||||
self.hide_hidden_clients();
|
||||
|
||||
self.clients
|
||||
.iter_current_screen()
|
||||
.iter_visible()
|
||||
.for_each(|(_, c)| self.xlib.move_resize_client(c));
|
||||
|
||||
self.clients
|
||||
|
@ -371,7 +375,17 @@ impl WindowManager {
|
|||
}
|
||||
|
||||
fn new_client(&mut self, window: Window) {
|
||||
self.clients.insert(Client::new_default(window)).unwrap();
|
||||
let client = if let Some(transient_window) = self.xlib.get_transient_for_window(window) {
|
||||
Client::new_transient(
|
||||
window,
|
||||
self.xlib.get_window_size(window).unwrap_or((100, 100)),
|
||||
transient_window,
|
||||
)
|
||||
} else {
|
||||
Client::new_default(window)
|
||||
};
|
||||
|
||||
self.clients.insert(client).unwrap();
|
||||
self.xlib.map_window(window);
|
||||
|
||||
self.focus_client(&window);
|
||||
|
|
27
src/xlib.rs
27
src/xlib.rs
|
@ -4,10 +4,10 @@ use std::{ffi::CString, rc::Rc};
|
|||
use x11::xlib::{
|
||||
self, Atom, ButtonPressMask, CWEventMask, ControlMask, EnterWindowMask, FocusChangeMask,
|
||||
LockMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, PointerMotionMask,
|
||||
PropertyChangeMask, ShiftMask, StructureNotifyMask, SubstructureNotifyMask,
|
||||
PropertyChangeMask, ShiftMask, Status, StructureNotifyMask, SubstructureNotifyMask,
|
||||
SubstructureRedirectMask, Window, XCloseDisplay, XConfigureRequestEvent, XDefaultScreen,
|
||||
XEvent, XInternAtom, XKillClient, XMapWindow, XOpenDisplay, XRaiseWindow, XRootWindow, XSync,
|
||||
XWarpPointer,
|
||||
XEvent, XGetTransientForHint, XInternAtom, XKillClient, XMapWindow, XOpenDisplay, XRaiseWindow,
|
||||
XRootWindow, XSync, XWarpPointer,
|
||||
};
|
||||
use xlib::GrabModeAsync;
|
||||
|
||||
|
@ -321,6 +321,27 @@ impl XLib {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_window_size(&self, window: Window) -> Option<(i32, i32)> {
|
||||
let mut wa =
|
||||
unsafe { std::mem::MaybeUninit::<xlib::XWindowAttributes>::zeroed().assume_init() };
|
||||
|
||||
if unsafe { xlib::XGetWindowAttributes(self.dpy(), window, &mut wa) != 0 } {
|
||||
Some((wa.width, wa.height))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_transient_for_window(&self, window: Window) -> Option<Window> {
|
||||
let mut transient_for: Window = 0;
|
||||
|
||||
if unsafe { XGetTransientForHint(self.dpy(), window, &mut transient_for) != 0 } {
|
||||
Some(transient_for)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure_window(&self, event: &XConfigureRequestEvent) {
|
||||
let mut wc = xlib::XWindowChanges {
|
||||
x: event.x,
|
||||
|
|
Loading…
Reference in a new issue