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) window: Window,
|
||||||
pub(crate) size: (i32, i32),
|
pub(crate) size: (i32, i32),
|
||||||
pub(crate) position: (i32, i32),
|
pub(crate) position: (i32, i32),
|
||||||
|
pub(crate) transient_for: Option<Window>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Client {
|
impl Default for Client {
|
||||||
|
@ -25,6 +26,7 @@ mod client {
|
||||||
window: 0,
|
window: 0,
|
||||||
size: (100, 100),
|
size: (100, 100),
|
||||||
position: (0, 0),
|
position: (0, 0),
|
||||||
|
transient_for: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +37,16 @@ mod client {
|
||||||
window,
|
window,
|
||||||
size,
|
size,
|
||||||
position,
|
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()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_transient(&self) -> bool {
|
||||||
|
self.transient_for.is_some()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for Client {
|
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();
|
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() {
|
client.position = {
|
||||||
vs.insert(&key);
|
(
|
||||||
|
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.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)
|
pub fn remove<K>(&mut self, key: &K)
|
||||||
|
@ -315,20 +345,47 @@ impl ClientState {
|
||||||
self.floating_clients.iter()
|
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)> {
|
pub fn iter_hidden(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
||||||
self.clients
|
self.iter_all_clients()
|
||||||
.iter()
|
.filter(move |&(k, _)| !self.is_client_visible(k))
|
||||||
.filter(move |&(k, _)| !self.virtual_screens.front().unwrap().contains(k))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_visible(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
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)> {
|
pub fn iter_current_screen(&self) -> impl Iterator<Item = (&u64, &Client)> {
|
||||||
self.clients
|
self.clients
|
||||||
.iter()
|
.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>
|
pub fn get<K>(&self, key: &K) -> ClientEntry<&Client>
|
||||||
|
@ -407,10 +464,18 @@ impl ClientState {
|
||||||
self.floating_clients.insert(key, client);
|
self.floating_clients.insert(key, client);
|
||||||
self.remove_from_virtual_screens(&key);
|
self.remove_from_virtual_screens(&key);
|
||||||
}
|
}
|
||||||
(None, Some(client)) => {
|
(None, Some(floating_client)) => {
|
||||||
self.clients.insert(key, client);
|
// transient clients cannot be tiled
|
||||||
if let Some(vs) = self.virtual_screens.front_mut() {
|
match floating_client.is_transient() {
|
||||||
vs.insert(&key);
|
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()));
|
let width = width / (1 + i32::from(!vs.aux.is_empty()));
|
||||||
|
|
||||||
// make sure we dont devide by 0
|
// make sure we dont devide by 0
|
||||||
|
// height is max height / number of clients in the stack
|
||||||
let master_height = height
|
let master_height = height
|
||||||
/ match NonZeroI32::new(vs.master.len() as i32) {
|
/ match NonZeroI32::new(vs.master.len() as i32) {
|
||||||
Some(i) => i.get(),
|
Some(i) => i.get(),
|
||||||
None => 1,
|
None => 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// height is max height / number of clients in the stack
|
||||||
let aux_height = height
|
let aux_height = height
|
||||||
/ match NonZeroI32::new(vs.aux.len() as i32) {
|
/ match NonZeroI32::new(vs.aux.len() as i32) {
|
||||||
Some(i) => i.get(),
|
Some(i) => i.get(),
|
||||||
|
@ -559,6 +626,7 @@ impl ClientState {
|
||||||
// add repeating height for each window and x pos for each window
|
// add repeating height for each window and x pos for each window
|
||||||
.zip(repeat(master_height).zip(repeat(0i32)))
|
.zip(repeat(master_height).zip(repeat(0i32)))
|
||||||
.chain(
|
.chain(
|
||||||
|
// same things for aux stack
|
||||||
vs.aux
|
vs.aux
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
|
30
src/state.rs
30
src/state.rs
|
@ -87,12 +87,12 @@ impl WindowManager {
|
||||||
|
|
||||||
self.add_keybind(KeyBinding::new(
|
self.add_keybind(KeyBinding::new(
|
||||||
self.xlib.make_key("T", Mod1Mask),
|
self.xlib.make_key("T", Mod1Mask),
|
||||||
|wm, _| wm.spawn("xterm", &[]),
|
|wm, _| wm.spawn("alacritty", &[]),
|
||||||
));
|
));
|
||||||
|
|
||||||
self.add_keybind(KeyBinding::new(
|
self.add_keybind(KeyBinding::new(
|
||||||
self.xlib.make_key("Return", Mod1Mask | ShiftMask),
|
self.xlib.make_key("Return", Mod1Mask | ShiftMask),
|
||||||
|wm, _| wm.spawn("xterm", &[]),
|
|wm, _| wm.spawn("alacritty", &[]),
|
||||||
));
|
));
|
||||||
|
|
||||||
self.add_keybind(KeyBinding::new(
|
self.add_keybind(KeyBinding::new(
|
||||||
|
@ -321,10 +321,6 @@ impl WindowManager {
|
||||||
Direction::Right => self.clients.rotate_right(),
|
Direction::Right => self.clients.rotate_right(),
|
||||||
}
|
}
|
||||||
|
|
||||||
self.clients
|
|
||||||
.iter_hidden()
|
|
||||||
.for_each(|(_, c)| self.xlib.hide_client(c));
|
|
||||||
|
|
||||||
self.arrange_clients();
|
self.arrange_clients();
|
||||||
|
|
||||||
// focus first client in all visible 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) {
|
fn arrange_clients(&mut self) {
|
||||||
let (width, height) = self.xlib.dimensions();
|
let (width, height) = self.xlib.dimensions();
|
||||||
self.clients.arrange_virtual_screen(width, height, Some(2));
|
self.clients.arrange_virtual_screen(width, height, Some(2));
|
||||||
|
|
||||||
|
self.hide_hidden_clients();
|
||||||
|
|
||||||
self.clients
|
self.clients
|
||||||
.iter_current_screen()
|
.iter_visible()
|
||||||
.for_each(|(_, c)| self.xlib.move_resize_client(c));
|
.for_each(|(_, c)| self.xlib.move_resize_client(c));
|
||||||
|
|
||||||
self.clients
|
self.clients
|
||||||
|
@ -371,7 +375,17 @@ impl WindowManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_client(&mut self, window: Window) {
|
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.xlib.map_window(window);
|
||||||
|
|
||||||
self.focus_client(&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::{
|
use x11::xlib::{
|
||||||
self, Atom, ButtonPressMask, CWEventMask, ControlMask, EnterWindowMask, FocusChangeMask,
|
self, Atom, ButtonPressMask, CWEventMask, ControlMask, EnterWindowMask, FocusChangeMask,
|
||||||
LockMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, PointerMotionMask,
|
LockMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, PointerMotionMask,
|
||||||
PropertyChangeMask, ShiftMask, StructureNotifyMask, SubstructureNotifyMask,
|
PropertyChangeMask, ShiftMask, Status, StructureNotifyMask, SubstructureNotifyMask,
|
||||||
SubstructureRedirectMask, Window, XCloseDisplay, XConfigureRequestEvent, XDefaultScreen,
|
SubstructureRedirectMask, Window, XCloseDisplay, XConfigureRequestEvent, XDefaultScreen,
|
||||||
XEvent, XInternAtom, XKillClient, XMapWindow, XOpenDisplay, XRaiseWindow, XRootWindow, XSync,
|
XEvent, XGetTransientForHint, XInternAtom, XKillClient, XMapWindow, XOpenDisplay, XRaiseWindow,
|
||||||
XWarpPointer,
|
XRootWindow, XSync, XWarpPointer,
|
||||||
};
|
};
|
||||||
use xlib::GrabModeAsync;
|
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) {
|
pub fn configure_window(&self, event: &XConfigureRequestEvent) {
|
||||||
let mut wc = xlib::XWindowChanges {
|
let mut wc = xlib::XWindowChanges {
|
||||||
x: event.x,
|
x: event.x,
|
||||||
|
|
Loading…
Reference in a new issue