diff --git a/src/config.rs b/src/config.rs index af0d8dc..faafcd4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -11,9 +11,9 @@ use fig::{Fig, FigError, Value}; use key::{get_arg, FUNC_MAP}; use libc::c_char; use x11::keysym::{ - XK_Return, XK_Tab, XK_b, XK_c, XK_comma, XK_d, XK_f, XK_h, XK_i, XK_j, - XK_k, XK_l, XK_m, XK_p, XK_period, XK_q, XK_space, XK_t, XK_0, XK_1, XK_2, - XK_3, XK_4, XK_5, XK_6, XK_7, XK_8, XK_9, + XK_Return, XK_Tab, XK_b, XK_c, XK_comma, XK_d, XK_f, XK_grave, XK_h, XK_i, + XK_j, XK_k, XK_l, XK_m, XK_p, XK_period, XK_q, XK_space, XK_t, XK_0, XK_1, + XK_2, XK_3, XK_4, XK_5, XK_6, XK_7, XK_8, XK_9, }; use x11::xlib::{Button1, Button2, Button3, ControlMask, Mod4Mask, ShiftMask}; @@ -474,10 +474,27 @@ static DMENUCMD: LazyLock> = LazyLock::new(|| { }); static TERMCMD: LazyLock> = LazyLock::new(|| vec!["st".into()]); -fn default_keys() -> [Key; 60] { +pub const SCRATCHPADNAME: &str = "scratchpad"; +pub static SCRATCHPADCMD: LazyLock> = LazyLock::new(|| { + vec![ + "st".into(), + "-t".into(), + SCRATCHPADNAME.into(), + "-g".into(), + "120x34".into(), + ] +}); + +fn default_keys() -> [Key; 61] { [ Key::new(MODKEY, XK_p, spawn, Arg::V(DMENUCMD.clone())), Key::new(S_MOD, XK_Return, spawn, Arg::V(TERMCMD.to_vec())), + Key::new( + MODKEY, + XK_grave, + togglescratch, + Arg::V(SCRATCHPADCMD.to_vec()), + ), Key::new(MODKEY, XK_b, togglebar, Arg::I(0)), Key::new(MODKEY, XK_j, focusstack, Arg::I(1)), Key::new(MODKEY, XK_k, focusstack, Arg::I(-1)), diff --git a/src/key_handlers.rs b/src/key_handlers.rs index 5323c69..75bc30f 100644 --- a/src/key_handlers.rs +++ b/src/key_handlers.rs @@ -19,7 +19,7 @@ use crate::{ getrootptr, height, is_visible, nexttiled, pop, recttomon, resize, resizebarwin, restack, sendevent, setfullscreen, unfocus, updatebarpos, width, xerror, xerrordummy, BH, CURSOR, DPY, HANDLER, MONS, MOUSEMASK, - ROOT, SELMON, SYSTRAY, TAGMASK, WMATOM, XNONE, + ROOT, SCRATCHTAG, SELMON, SYSTRAY, TAGMASK, WMATOM, XNONE, }; use rwm::{Arg, Client, Monitor}; @@ -634,6 +634,8 @@ pub(crate) fn spawn(arg: *const Arg) { argv.push((*SELMON).num.to_string()); } + (*SELMON).tagset[(*SELMON).seltags as usize] &= !*SCRATCHTAG; + let mut cmd = Command::new(argv[0].clone()); let cmd = if argv.len() > 1 { cmd.args(&argv[1..]) } else { &mut cmd }; @@ -670,3 +672,34 @@ pub(crate) fn fullscreen(_: *const Arg) { setfullscreen((*SELMON).sel, !(*(*SELMON).sel).isfullscreen) } } + +pub(crate) fn togglescratch(arg: *const Arg) { + unsafe { + let mut c: *mut Client; + let mut found = false; + cfor!(( + c = (*SELMON).clients; + !c.is_null(); + c = (*c).next) { + found = ((*c).tags & *SCRATCHTAG) != 0; + if found { + break; + } + }); + if found { + let newtagset = + (*SELMON).tagset[(*SELMON).seltags as usize] ^ *SCRATCHTAG; + if newtagset != 0 { + (*SELMON).tagset[(*SELMON).seltags as usize] = newtagset; + focus(null_mut()); + arrange(SELMON); + } + if is_visible(c) { + focus(c); + restack(SELMON); + } + } else { + spawn(arg); + } + } +} diff --git a/src/main.rs b/src/main.rs index 51bea57..5e8a6a0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ //! tiling window manager based on dwm use std::cmp::max; -use std::ffi::{c_char, c_int, c_uint, c_ulong, CStr}; +use std::ffi::{c_char, c_int, c_uint, c_ulong, CStr, CString}; use std::io::Read; use std::mem::size_of_val; use std::mem::{size_of, MaybeUninit}; @@ -2412,6 +2412,26 @@ fn manage(w: Window, wa: *mut xlib::XWindowAttributes) { (*c).y = max((*c).y, (*(*c).mon).wy as i32); (*c).bw = CONFIG.borderpx as i32; + // TODO pretty sure this doesn't work with pertags, which explains some + // behavior I saw before in dwm. probably need to operate on + // selmon.pertag.tags[selmon.pertag.curtag]. + // + // TODO I'm also pretty sure this is _not_ the right way to be handling + // this. checking the name of the window and applying these rules seems + // like something meant to be handled by RULES + (*SELMON).tagset[(*SELMON).seltags as usize] &= !*SCRATCHTAG; + let scratchname = match CString::new(config::SCRATCHPADNAME) { + Ok(s) => s.as_ptr(), + Err(_) => null_mut(), + }; + if libc::strcmp((*c).name.as_ptr(), scratchname) == 0 { + (*c).tags = *SCRATCHTAG; + (*(*c).mon).tagset[(*(*c).mon).seltags as usize] |= (*c).tags; + (*c).isfloating = true; + (*c).x = (*(*c).mon).wx + (*(*c).mon).ww / 2 - width(c) / 2; + (*c).y = (*(*c).mon).wy + (*(*c).mon).wh / 2 - height(c) / 2; + } + log::trace!("manage: XWindowChanges"); let mut wc = xlib::XWindowChanges { x: 0, @@ -2749,6 +2769,8 @@ static TAGMASK: LazyLock = LazyLock::new(|| (1 << CONFIG.tags.len()) - 1); const BUTTONMASK: i64 = ButtonPressMask | ButtonReleaseMask; const MOUSEMASK: i64 = BUTTONMASK | PointerMotionMask; +static SCRATCHTAG: LazyLock = LazyLock::new(|| 1 << CONFIG.tags.len()); + fn updatetitle(c: *mut Client) { log::trace!("updatetitle"); unsafe {