3
\$\begingroup\$

I have written a small program that swallows windows. It works in X.org using the xcb library. This is my first rust program but I have lots of other programming experience.

Any general improvements are great, but a specific problem I've been having is that if the program exits too quickly (child process is short) there's a chance the parent window isn't remapped. I have gotten around this by using the sleep function, but this is only a quick fix and slows the program down noticeably.

I have tried many different features of xcb like set_input_focus, change_window_attribute and wait_for_event among others. I have also tried different logical solutions like running map_window multiple times or running the command through sh -c.

Here is the source code:

extern crate xcb;
use std::{env, process, thread, time};

fn main() {
    let (conn, _screen_num) = xcb::Connection::connect(None).unwrap();
    let win = xcb::get_input_focus(&conn).get_reply().unwrap().focus();
    let args: Vec<String> = env::args().collect();
    let mut command = process::Command::new(&args[1]);
    command.args(&args[2 .. args.len()]);

    xcb::unmap_window(&conn, win);
    conn.flush();
    let stat: i32 = command.status().unwrap().code().unwrap();
    xcb::map_window(&conn, win);
    conn.flush();

    thread::sleep(time::Duration::from_millis(10));

    process::exit(stat);
}

And the GitHub link: https://github.com/EmperorPenguin18/gobble

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Would xcb::map_window_checked(&conn, win).request_check().unwrap() help you? \$\endgroup\$
    – Cryptjar
    Jun 13, 2021 at 16:25

2 Answers 2

4
\$\begingroup\$

Welcome to Rust, and welcome to Code Review!

I do not have a Linux device at hand, so I will focus more on the code itself rather than the functionality.

extern crate

In general, extern crate is not needed since the 2018 edition, barring some exceptional edge cases. Since the examples on GitHub contain this directive, and I can't test the code, I assume extern crate is necessary here, but keep in mind that it isn't needed for the majority of crates.

Formatting

Your code is compliant with the Official Rust Style Guide for the most part — the only difference is shown here:

     let win = xcb::get_input_focus(&conn).get_reply().unwrap().focus();
     let args: Vec<String> = env::args().collect();
     let mut command = process::Command::new(&args[1]);
-    command.args(&args[2 .. args.len()]);
+    command.args(&args[2..args.len()]);

     xcb::unmap_window(&conn, win);
     conn.flush();

You can use cargo fmt to automatically format your code.

Spawning the process

I feel that configuring and executing the command all at once is more readable:

let args = env::args().collect();
let stat: i32 = process::Command::new(&args[1])
    .args(&args[2..])
    .status()
    .unwrap()
    .code()
    .unwrap();

Note that &args[2..] is equivalent to &args[2..args.len()].

Error handling

I recommend using the ? operator and returning a Result instead of unwrapping all the time. The anyhow crate comes in handy.

\$\endgroup\$
0
\$\begingroup\$

Thanks to @L. F. and @Cryptjar for their answers. It seems a combination of both fixed the problem for me. Using xcb::map_window_checked(&conn, win).request_check()? causes an error to be output instead of the program just crashing. For reference, make sure to use the anyhow crate like this: fn main() -> Result<(), anyhow::Error> {

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.