├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md └── src ├── killw.rs ├── lsw.rs ├── mapw.rs ├── pfw.rs ├── util.rs ├── wattr.rs ├── wrs.rs └── wtf.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "wmutils-rust" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "clap 2.2.5 (registry+https://github.com/rust-lang/crates.io-index)", 6 | "xcb 0.7.2 (git+https://github.com/rtbo/rust-xcb.git)", 7 | ] 8 | 9 | [[package]] 10 | name = "ansi_term" 11 | version = "0.7.2" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | 14 | [[package]] 15 | name = "bitflags" 16 | version = "0.4.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | 19 | [[package]] 20 | name = "clap" 21 | version = "2.2.5" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | dependencies = [ 24 | "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", 25 | "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 26 | "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", 27 | "strsim 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 28 | "unicode-width 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 29 | "vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", 30 | ] 31 | 32 | [[package]] 33 | name = "libc" 34 | version = "0.2.9" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | 37 | [[package]] 38 | name = "log" 39 | version = "0.3.6" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | 42 | [[package]] 43 | name = "strsim" 44 | version = "0.4.0" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | 47 | [[package]] 48 | name = "unicode-width" 49 | version = "0.1.3" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | 52 | [[package]] 53 | name = "vec_map" 54 | version = "0.6.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | 57 | [[package]] 58 | name = "xcb" 59 | version = "0.7.2" 60 | source = "git+https://github.com/rtbo/rust-xcb.git#9d49b91a2d87ed21b4ef5c80baf2aef68d2d7c32" 61 | dependencies = [ 62 | "libc 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", 63 | "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 64 | ] 65 | 66 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wmutils-rust" 3 | version = "0.1.0" 4 | authors = ["Adam "] 5 | 6 | [dependencies] 7 | clap = "2.2.5" 8 | 9 | [dependencies.xcb] 10 | git = "https://github.com/rtbo/rust-xcb.git" 11 | 12 | [[bin]] 13 | name = "pfw" 14 | path = "src/pfw.rs" 15 | 16 | [[bin]] 17 | name = "wtf" 18 | path = "src/wtf.rs" 19 | 20 | [[bin]] 21 | name = "mapw" 22 | path = "src/mapw.rs" 23 | 24 | [[bin]] 25 | name = "killw" 26 | path = "src/killw.rs" 27 | 28 | [[bin]] 29 | name = "wattr" 30 | path = "src/wattr.rs" 31 | 32 | [[bin]] 33 | name = "wrs" 34 | path = "src/wrs.rs" 35 | 36 | [[bin]] 37 | name = "lsw" 38 | path = "src/lsw.rs" 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | wmutils (in Rust) 2 | ================= 3 | 4 | This is a rewrite of [wmutils](https://github.com/wmutils/core) in Rust. 5 | I'm mostly doing it to learn how XCB (and its Rust binding) work. 6 | 7 | - [ ] chwb - change window's border 8 | - [ ] chwso - change window's stacking order 9 | - [ ] ignw - ignore/unignore window 10 | - [x] killw - kill windows 11 | - [x] lsw - list windows 12 | - [x] mapw - map/unmap windows 13 | - [x] pfw - print focused window 14 | - [x] wattr - show window's attributes 15 | - [ ] wmp - move the mouse pointer 16 | - [ ] wmv - move a window 17 | - [ ] wname - get a window's name 18 | - [ ] wrs - resize a window 19 | - [x] wtf - focus a window 20 | - [ ] wtp - teleport a window 21 | -------------------------------------------------------------------------------- /src/killw.rs: -------------------------------------------------------------------------------- 1 | extern crate xcb; 2 | extern crate clap; 3 | 4 | use clap::{App, Arg}; 5 | 6 | pub mod util; 7 | 8 | fn main() { 9 | let args = App::new("killw") 10 | .about("kill windows") 11 | .arg(Arg::with_name("parent") 12 | .short("p") 13 | .help("Kill the parent application of the window instead of the window itself")) 14 | .arg(Arg::with_name("wid") 15 | .multiple(true) 16 | .required(true)) 17 | .get_matches(); 18 | 19 | let connection = util::init_xcb("killw"); 20 | let wids = args.values_of("wid").unwrap(); // Unwrap is fine, the arg is required 21 | 22 | for wid in wids { 23 | let wid = util::get_window_id(&wid); 24 | 25 | if args.is_present("parent") { 26 | xcb::kill_client(&connection, wid); 27 | } else { 28 | xcb::destroy_window(&connection, wid); 29 | } 30 | } 31 | 32 | connection.flush(); 33 | } 34 | -------------------------------------------------------------------------------- /src/lsw.rs: -------------------------------------------------------------------------------- 1 | extern crate xcb; 2 | extern crate clap; 3 | mod util; 4 | 5 | use clap::{Arg, App}; 6 | 7 | #[derive(Copy,Clone)] 8 | struct Flags { 9 | all : bool, 10 | hidden: bool, 11 | ignore: bool, 12 | } 13 | 14 | impl Flags { 15 | fn none(self) -> bool { 16 | !self.all 17 | && !self.hidden 18 | && !self.ignore 19 | } 20 | } 21 | 22 | fn main() { 23 | let args = App::new("lsw") 24 | .about("list child windows") 25 | .arg(Arg::with_name("a").short("a").help("List all windows.")) 26 | .arg(Arg::with_name("r").short("r").help("Print the ID of the root window.")) 27 | .arg(Arg::with_name("o").short("o").help("List windows whose override_redirect attribute is set to 1.")) 28 | .arg(Arg::with_name("u").short("u").help("List unmapped (invisible) windows.")) 29 | .arg(Arg::with_name("wid").multiple(true)) 30 | .get_matches(); 31 | 32 | // Initialize xcb values 33 | let conn = util::init_xcb("lsw"); 34 | let setup = conn.get_setup(); 35 | let screen = util::get_screen(&setup); 36 | let root = screen.root(); 37 | 38 | // Get all passed window ids 39 | let mut wids = match args.values_of("") { 40 | Some(ids) => ids.map(util::get_window_id).collect(), 41 | None => vec![screen.root()] 42 | }; 43 | 44 | // Print requested info 45 | if args.is_present("r") { 46 | println!("0x{:08x}", root); 47 | return; 48 | } 49 | 50 | let flags = Flags { 51 | all : args.is_present("a"), 52 | hidden: args.is_present("u"), 53 | ignore: args.is_present("o"), 54 | }; 55 | 56 | // Print the children window IDs if applicable 57 | for wid in wids { 58 | let tree = util::get_query_tree(&conn, wid); 59 | for &child in tree.children() { 60 | if should_print(&conn, child, flags) { 61 | println!("0x{:08x}", child); 62 | } 63 | } 64 | } 65 | } 66 | 67 | fn should_print(conn: &xcb::Connection, window: xcb::Window, flags: Flags) -> bool { 68 | flags.all 69 | || (!util::mapped(conn, window) && flags.hidden) 70 | || ( util::ignore(conn, window) && flags.ignore) 71 | || util::mapped(conn, window) 72 | && !util::ignore(conn, window) 73 | && flags.none() 74 | } 75 | -------------------------------------------------------------------------------- /src/mapw.rs: -------------------------------------------------------------------------------- 1 | extern crate xcb; 2 | extern crate clap; 3 | 4 | use clap::{App, Arg, ArgGroup}; 5 | 6 | pub mod util; 7 | 8 | fn main() { 9 | let args = App::new("mapw") 10 | .about("map or unmap windows") 11 | .arg(Arg::with_name("map").short("m").help("Map (show) wid.")) 12 | .arg(Arg::with_name("toggle").short("t").help("Toggle wid's visibility.")) 13 | .arg(Arg::with_name("unmap").short("u").help("Unmap (hide) wid.")) 14 | .group(ArgGroup::with_name("options").required(true).args(&[ 15 | "map", "toggle", "unmap" 16 | ])) 17 | .arg(Arg::with_name("wid") 18 | .multiple(true) 19 | .required(true)) 20 | .get_matches(); 21 | 22 | let connection = util::init_xcb("mapw"); 23 | let wids = args.values_of("wid").unwrap(); // Unwrap is fine, the arg is required 24 | 25 | let action: fn(&xcb::Connection, xcb::Window) = 26 | if args.is_present("map") { map } 27 | else if args.is_present("unmap") { unmap } 28 | else { toggle }; 29 | 30 | for wid in wids { 31 | let wid = util::get_window_id(wid); 32 | action(&connection, wid); 33 | } 34 | 35 | connection.flush(); 36 | } 37 | 38 | fn map(connection: &xcb::Connection, window: xcb::Window) { 39 | xcb::map_window(connection, window); 40 | } 41 | 42 | fn unmap(connection: &xcb::Connection, window: xcb::Window) { 43 | xcb::unmap_window(connection, window); 44 | } 45 | 46 | fn toggle(connection: &xcb::Connection, window: xcb::Window) { 47 | if util::mapped(connection, window) { 48 | xcb::unmap_window(connection, window); 49 | } 50 | else { 51 | xcb::map_window(connection, window); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/pfw.rs: -------------------------------------------------------------------------------- 1 | // Implementation of wmutils' pfw (print focused window) 2 | 3 | extern crate xcb; 4 | extern crate clap; 5 | 6 | use clap::App; 7 | 8 | pub mod util; 9 | 10 | fn main() { 11 | App::new("pfw").about("print focused window").get_matches(); 12 | 13 | let connection = util::init_xcb("pfw"); 14 | 15 | let c = xcb::get_input_focus(&connection); 16 | let r = c.get_reply(); 17 | 18 | match r { 19 | Ok(r) => println!("0x{:08x}", r.focus()), 20 | Err(e) => println!("Error: {:?}", e) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | extern crate xcb; 2 | 3 | use xcb::base; 4 | use xcb::xproto; 5 | use std::process; 6 | 7 | pub fn init_xcb(programname: &str) -> base::Connection { 8 | match base::Connection::connect(None) { 9 | Ok((conn, _)) => conn, 10 | Err(_) => { 11 | println!("{}: Unable to connect to the X server", programname); 12 | process::exit(1); 13 | } 14 | } 15 | } 16 | 17 | pub fn get_screen<'a>(setup: &'a xproto::Setup) -> xproto::Screen<'a> { 18 | setup.roots().next().expect("Lost connection to X server") 19 | } 20 | 21 | pub fn exists(conn: &base::Connection, window: xproto::Window) -> bool { 22 | let win_attrib_cookie = xproto::get_window_attributes(&conn, window); 23 | let win_attrib_cookie_reply_result = win_attrib_cookie.get_reply(); 24 | 25 | match win_attrib_cookie_reply_result { 26 | Ok(_) => true, 27 | Err(_) => false, 28 | } 29 | } 30 | 31 | pub fn mapped(conn: &xcb::Connection, window: xcb::Window) -> bool { 32 | let attrs = xcb::get_window_attributes(&conn, window).get_reply(); 33 | match attrs { 34 | Ok(attrs) => attrs.map_state() as u32 == xcb::MAP_STATE_VIEWABLE, 35 | _ => false 36 | } 37 | } 38 | 39 | pub fn ignore(conn: &xcb::Connection, window: xcb::Window) -> bool { 40 | let attrs = xcb::get_window_attributes(&conn, window).get_reply(); 41 | match attrs { 42 | Ok(attrs) => attrs.override_redirect(), 43 | _ => false 44 | } 45 | } 46 | 47 | pub fn get_window_id(input: &str) -> xproto::Window { 48 | let window = if input.starts_with("0x") { 49 | &input[2..] 50 | } else { 51 | input 52 | }; 53 | 54 | match u32::from_str_radix(window, 16) { 55 | Ok(val) => val, 56 | Err(_) => 0, 57 | } 58 | } 59 | 60 | pub fn get_query_tree(conn: &xcb::Connection, window: xcb::Window) -> xcb::QueryTreeReply { 61 | xcb::query_tree(conn, window).get_reply().expect("no such window") 62 | } 63 | -------------------------------------------------------------------------------- /src/wattr.rs: -------------------------------------------------------------------------------- 1 | extern crate xcb; 2 | 3 | use std::env; 4 | use std::process; 5 | use xcb::base; 6 | use xcb::xproto; 7 | 8 | pub mod util; 9 | 10 | enum Attribute { 11 | W, 12 | H, 13 | X, 14 | Y, 15 | B, 16 | } 17 | 18 | fn usage(programname: &String) { 19 | println!("Usage: {} [-h] [bmiowhxy] ", programname); 20 | process::exit(1); 21 | } 22 | 23 | fn get_attribute(conn: &base::Connection, win: xproto::Window, attr: Attribute) -> i32 { 24 | if ! util::exists(&conn, win) { 25 | println!("0x{:08x}: No such window", win); 26 | process::exit(1); 27 | } 28 | 29 | let geometry_cookie = xproto::get_geometry(&conn, win); 30 | let geometry_cookie_reply = geometry_cookie.get_reply().unwrap(); 31 | 32 | match attr { 33 | Attribute::X => geometry_cookie_reply.x() as i32, 34 | Attribute::Y => geometry_cookie_reply.y() as i32, 35 | Attribute::W => geometry_cookie_reply.width() as i32, 36 | Attribute::H => geometry_cookie_reply.height() as i32, 37 | Attribute::B => geometry_cookie_reply.border_width() as i32, 38 | } 39 | } 40 | 41 | fn main() { 42 | let programname = env::args().nth(0).unwrap_or_else(|| String::new()); 43 | let args: Vec<_> = env::args().collect(); 44 | if args.len() < 2 || args[1] == "-h" || args[1] == "--help" { 45 | usage(&programname); 46 | } 47 | 48 | let connection = util::init_xcb(&programname); 49 | 50 | if args.len() == 2 { 51 | let win = util::get_window_id(&args[1]); 52 | match util::exists(&connection, win) { 53 | true => process::exit(0), 54 | false => process::exit(1), 55 | } 56 | } 57 | 58 | let win = util::get_window_id(&args[2]); 59 | 60 | let mut iter = args[1].chars().peekable(); 61 | while let Some(c) = iter.next() { 62 | match c { 63 | 'i' => print!("0x{:08x}", win), 64 | 'b' => print!("{}", get_attribute(&connection, win, Attribute::B)), 65 | 'h' => print!("{}", get_attribute(&connection, win, Attribute::H)), 66 | 'x' => print!("{}", get_attribute(&connection, win, Attribute::X)), 67 | 'y' => print!("{}", get_attribute(&connection, win, Attribute::Y)), 68 | 'w' => print!("{}", get_attribute(&connection, win, Attribute::W)), 69 | 'o' => if util::ignore(&connection, win) { 70 | std::process::exit(0); 71 | } else { 72 | std::process::exit(1); 73 | }, 74 | 'm' => if util::mapped(&connection, win) { 75 | std::process::exit(0); 76 | } else { 77 | std::process::exit(1); 78 | }, 79 | _ => usage(&programname), 80 | } 81 | 82 | if iter.peek() == None { 83 | println!(""); 84 | } else { 85 | print!(" "); 86 | } 87 | } 88 | 89 | connection.flush(); 90 | } 91 | -------------------------------------------------------------------------------- /src/wrs.rs: -------------------------------------------------------------------------------- 1 | extern crate xcb; 2 | #[macro_use] 3 | extern crate clap; 4 | 5 | use clap::{App, Arg}; 6 | 7 | pub mod util; 8 | 9 | fn resize(conn: &xcb::Connection, win: xcb::Window, absolute: bool, mut x: i16, mut y: i16) { 10 | let setup = conn.get_setup(); 11 | let screen = util::get_screen(&setup); 12 | 13 | let geometry_cookie = xcb::get_geometry(&conn, win); 14 | let geometry_cookie_reply_result = geometry_cookie.get_reply(); 15 | 16 | let reply = match geometry_cookie_reply_result { 17 | Ok(v) => v, 18 | Err(_) => return, 19 | }; 20 | 21 | let window_width = reply.width() as i16; 22 | let window_height = reply.height() as i16; 23 | let window_x = reply.x(); 24 | let window_y = reply.y(); 25 | let window_border = reply.border_width() as i16; 26 | let screen_width = screen.width_in_pixels() as i16; 27 | let screen_height = screen.height_in_pixels() as i16; 28 | 29 | if absolute { 30 | x -= window_x + window_width; 31 | y -= window_y + window_height; 32 | } 33 | 34 | if window_x + window_width + 2 * window_border + x > screen_width { 35 | x = screen_width - (window_x + window_width + 2 * window_border) 36 | } 37 | 38 | if window_y + window_height + 2 * window_border + y > screen_height { 39 | y = screen_height - (window_y + window_height + 2 * window_border) 40 | } 41 | 42 | // This shows that the values are being interpreted correctly 43 | // println!("Old width: {}", window_width); 44 | // println!("Old height: {}", window_height); 45 | // println!("New width: {}", window_width + x); 46 | // println!("New height: {}", window_height + y); 47 | 48 | // xcb::configure_window(conn, 49 | // win, 50 | // &[(xcb::CONFIG_WINDOW_WIDTH as u16, 500 as u32), 51 | // (xcb::CONFIG_WINDOW_HEIGHT as u16, 300 as u32), 52 | // (xcb::STACK_MODE_ABOVE as u16, xcb::CONFIG_WINDOW_STACK_MODE as u32)]); 53 | 54 | xcb::configure_window(conn, 55 | win, 56 | &[(xcb::CONFIG_WINDOW_WIDTH as u16, (window_width + x) as u32), 57 | (xcb::CONFIG_WINDOW_HEIGHT as u16, (window_height + y) as u32), 58 | (xcb::STACK_MODE_ABOVE as u16, xcb::CONFIG_WINDOW_STACK_MODE as u32)]); 59 | } 60 | 61 | fn main() { 62 | let args = App::new("wrs") 63 | .about("resize windows") 64 | .arg(Arg::with_name("absolute").short("a")) 65 | .arg(Arg::with_name("x").required(true)) 66 | .arg(Arg::with_name("y").required(true)) 67 | .arg(Arg::with_name("wid") 68 | .required(true) 69 | .multiple(true)) 70 | .get_matches(); 71 | 72 | 73 | let absolute = args.is_present("absolute"); 74 | let x = value_t!(args.value_of("x"), i16).unwrap_or_else(invalid_number); 75 | let y = value_t!(args.value_of("y"), i16).unwrap_or_else(invalid_number); 76 | let wids = args.values_of("wid").unwrap(); // Unwrap is fine, the arg is required 77 | 78 | let connection = util::init_xcb("wrs"); 79 | 80 | for wid in wids { 81 | let wid = util::get_window_id(wid); 82 | resize(&connection, wid, absolute, x, y); 83 | } 84 | connection.flush(); 85 | } 86 | 87 | fn invalid_number(_: E) -> T { 88 | use std::io::Write; 89 | write!(::std::io::stderr(), "invalid number format\n").unwrap(); 90 | ::std::process::exit(1); 91 | } 92 | -------------------------------------------------------------------------------- /src/wtf.rs: -------------------------------------------------------------------------------- 1 | extern crate xcb; 2 | extern crate clap; 3 | 4 | use clap::{App, Arg}; 5 | 6 | pub mod util; 7 | 8 | fn main() { 9 | let args = App::new("wtf") 10 | .about("transfer window focus") 11 | .arg(Arg::with_name("wid").required(true)) 12 | .get_matches(); 13 | 14 | let wid = args.value_of("wid").unwrap(); // Unwrap is fine, the arg is required 15 | let wid = util::get_window_id(wid); 16 | 17 | let connection = util::init_xcb("wtf"); 18 | 19 | xcb::set_input_focus(&connection, xcb::INPUT_FOCUS_POINTER_ROOT as u8, wid, xcb::TIME_CURRENT_TIME); 20 | 21 | connection.flush(); 22 | } 23 | --------------------------------------------------------------------------------