kill-desktop and TUIs
kill-desktop tries to get your "X" applications to exit cleanly, such that you can shutdown, or reboot.
"Watch" the "demo" in the repository readme, or try it out for yourself:
cargo install kill-desktop
Many people just reboot. This risks losing unsaved work, such as documents, the play position in your media player, or even the shell history in your shell.
This feature is typically built in to desktop environments, but somewhat lacking in the more minimalist of linux window managers, such as my favourite, i3wm.
Even the more complex solutions, such as the system built into Windows, do not deal well with naughty applications; ones that will just go hide in the tray when you try to close them, or that show dialogs a while after you asked them to exit.
kill-desktop
attempts to solve this problem by keeping track of the state
of the system, and offering you ways to progress. If one of these naughty
hiding applications merely hides when you close the window, kill-desktop
doesn't forget. It tracks the process of that window, waiting for it to go
away. If it is not going away, you are able to ask the process to exit. Or
just shut down. It's probably a bad application anyway.
Interesting learnings from this project:
Firstly, writing an interface was a bit of a pain. I wanted to be able to
prompt the user for an action, but also be able to show them updates. It is
not possible to do this without
threads,
as there is no way to do a non-blocking read from stdin
. This surprised me.
You can't even read a single character (think Continue? y/n
) without messing
with the terminal settings, which needs very low level, non-portable libraries.
There are nicely packaged solutions to this problem, like
termion's async_stdin
but this ended up messing with the terminal more than required (it puts it all
the way into raw
mode, instead of stopping at -icanon
). I
wrote my own.
Secondly, it's amazing how a relatively simple application can end up tracking more state than expected, and manually diffing that state.
I also spent time moving errors to be part of the domain, which isn't even fully surfaced yet. It amazes me how much code ends up being dedicated to error handling, even in a language with excellent terse error handling. (Terminology from Feathers.)
It's also ended up with nine dependencies, although between four and six of those are for loading the (trivial) config file, which could be done better.