use crate::{
builtin::actions::{key_handler, modify_with},
core::{bindings::KeyEventHandler, layout::LayoutStack, State},
util::spawn,
x::{atom::Atom, property::Prop, ClientConfig, XConn, XConnExt},
Error, Result, Xid,
};
use tracing::{debug, error};
mod dynamic_select;
#[doc(inline)]
pub use dynamic_select::*;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum FullScreenAction {
Remove,
Add,
Toggle,
}
pub fn set_fullscreen_state<X: XConn>(
id: Xid,
action: FullScreenAction,
state: &mut State<X>,
x: &X,
) -> Result<()> {
use FullScreenAction::*;
let net_wm_state = Atom::NetWmState.as_ref();
let full_screen = x.intern_atom(Atom::NetWmStateFullscreen.as_ref())?;
let mut wstate = match x.get_prop(id, net_wm_state) {
Ok(Some(Prop::Cardinal(vals))) => vals,
_ => vec![],
};
let currently_fullscreen = wstate.contains(&full_screen);
debug!(%currently_fullscreen, ?action, %id, "setting fullscreen state");
if action == Add || (action == Toggle && !currently_fullscreen) {
let r = state
.client_set
.screen_for_client(&id)
.ok_or(Error::UnknownClient(id))?
.r;
state.client_set.float(id, r)?;
wstate.push(*full_screen);
x.set_client_config(id, &[ClientConfig::BorderPx(0)])?; } else if currently_fullscreen && (action == Remove || action == Toggle) {
state.client_set.sink(&id);
wstate.retain(|&val| val != *full_screen);
x.set_client_config(id, &[ClientConfig::BorderPx(state.config.border_width)])?;
}
x.set_prop(id, net_wm_state, Prop::Cardinal(wstate))?;
x.refresh(state)
}
pub fn toggle_fullscreen<X: XConn>() -> Box<dyn KeyEventHandler<X>> {
key_handler(|state, x: &X| {
let id = match state.client_set.current_client() {
Some(&id) => id,
None => return Ok(()),
};
set_fullscreen_state(id, FullScreenAction::Toggle, state, x)
})
}
pub fn create_or_switch_to_workspace<X>(
get_name: fn() -> Option<String>,
layouts: LayoutStack,
) -> Box<dyn KeyEventHandler<X>>
where
X: XConn,
{
modify_with(move |cs| {
if let Some(name) = get_name() {
_ = cs.add_workspace(&name, layouts.clone());
cs.focus_tag(&name);
}
})
}
pub fn switch_to_workspace<X>(
select_name: fn(&[String]) -> Option<String>,
) -> Box<dyn KeyEventHandler<X>>
where
X: XConn,
{
modify_with(move |cs| {
let tags = cs.ordered_tags();
if let Some(name) = select_name(&tags) {
cs.focus_tag(&name);
}
})
}
pub fn focus_or_spawn<X>(class: &'static str, command: &'static str) -> Box<dyn KeyEventHandler<X>>
where
X: XConn,
{
key_handler(move |s: &mut State<X>, x: &X| {
let mut client = None;
for &id in s.client_set.clients() {
if let Some(Prop::UTF8String(classes)) = x.get_prop(id, Atom::WmClass.as_ref())? {
if classes.iter().any(|s| s == class) {
client = Some(id);
break;
}
}
}
x.modify_and_refresh(s, |cs| {
if let Some(id) = client {
cs.focus_client(&id)
} else if let Err(e) = spawn(command) {
error!(%e, %command, "unable to spawn program")
}
})
})
}