Testing results (though there's not really much to test there) and comments on the code would be much appreciated. I've been using it without a problem up until now.

Two additional remarks: While the README file on GitHub states the script can currently be used with Openbox or JWM only, it should be able work with any window manager that can be queried by wmctrl and offers an exit switch as a command line option. It would simply have to be added. Also, I deliberately chose xmessage for the GUI for two reasons: One, it's part of X and doesn't depend on anything else. Two, my plan is to expand and re-write the whole script in Python and use Tk for the GUI eventually. So it doesn't have to look totally beautiful yet.

Re: Warn on exit script for Openbox, JWM etc.

Re: Warn on exit script for Openbox, JWM etc.

fsmithred wrote:

I'm not understanding the behavior of cut.

It is a bit strange indeed. Thanks for spotting that.

The problem seems to have two causes: First, the second column of wmctrl's output, which displays the number of the virtual workspace a window is on, has a different width for sticky windows (those that are visible across all workspaces), since they are identified by a value of -1. (The same goes for windows with a workspace number above 10.)

Then, as ralph.ronnquist has already pointed out, cut -d " " doesn't seem to collapse multiple spaces into one. What it seems to do instead is a bit weird: If there is only one space between two printable character strings, it obviously takes that space as a delimiter, e.g.:

Re: Warn on exit script for Openbox, JWM etc.

Trying to use sed, I realized that it's probably not the right tool for what I want to do here. cut is actually fine. You just have to use it properly, which would mean using cut -c 15-18 in this case.

But then I thought: This is nothing but a simple substring extraction, so there should be a way to do it using parameter expansion. And there is: Once a line printed by wmctrl has been assigned to a variable, you can just use "${line:14:4}", where the first number is the starting position and the second the substring's character count.

Re: Warn on exit script for Openbox, JWM etc.

Right. I already noticed this is causing problems when, at one point, the script would not exit the window manager even though I had put all remaining windows on the ignore list. The reason was that one text editor window I was running had a 5-digit process id.

After searching the web for a bit, I found a really neat solution, allowing me to go back to using the initial cut command. As mentioned above, the problem with cut is that it cannot collapse multiple spaces into single ones. But you can have tr do that before you pipe the output to cut:

wmctrl -l -p | tr -s ' ' | cut -d ' ' -f 3

tr -s, as the manual page states, "replace(s) each input sequence of a repeated character (...) with a single occurrence of that character."

Re: Warn on exit script for Openbox, JWM etc.

Another problem I've been facing with that script is that executing it involves killing its parent process. When running the script directly (meaning not from a terminal window), the process tree will look like this:

Re: Warn on exit script for Openbox, JWM etc.

After some more thinking, tinkering and reading around, I found a solution to this problem that's pretty workable. Let me get there step by step.

The idea was to somehow have the window manager's exit command invoked as independent from the currently running script but also put it on hold as long as that script is running. First, this implies that the hold action and the exit action need to be implemented as one command. Second, to run that command as independent from the current script, it has to be put in the background on invocation, which is done by appending an ampersand to the end of the line.

A very primitive and generally unreliable way to achieve this is using the sleep command and the && operator:

#!/bin/bash
#
# procman1
cmd_name=${0##*/} # Get basename of the entered command
cmd_pid=$$ # Get PID of this script
sleep 5 && openbox --exit & # Create a process in the background that will wait
# for 5 seconds and then exit Openbox.
printf "${cmd_name}($cmd_pid): I've given order to kill Openbox. This will \
happen shortly after I'm done.\n"
exit

(On a side note: I found that making the wait-and-exit routine a compound command by enclosing it in {} is not necessary because && already ties both commands together.)

Running this script and asking the shell for its exit status after it has finished shows that it is able to exit cleanly before the window manager is closed down:

$ ./devel/shell/procman/procman1 && echo $?
procman1(4742): I've given order to kill Openbox. This will happen shortly after I'm done.
0

The problem here is that allowing the current script to finish is achieved simply by waiting 5 seconds before shutting down Openbox. And while there's a very good chance this script is never going to take longer than 5 seconds to complete, scheduling a command execution based on the estimated execution time of another – instead of actually checking if that process is still alive – is fundamentally risky and should not be done.

The wait-and-exit routine should rather stay awake and explicitly wait for the script to finish. Now, waiting for processes to complete before executing anything else can be achieved by issuing the wait command. But that has its limitations: It can only handle child processes of the current shell. If sleep is simply replaced by wait (plus PID of the running script), the shell will complain about that.

#!/bin/bash
#
# procman2
cmd_name=${0##*/} # Get basename of the entered command
cmd_pid=$$ # Get PID of this script
wait $cmd_pid && openbox --exit & # Create a process in the background that
# will wait for this script to finish and
# then exit Openbox.
printf "${cmd_name}($cmd_pid): I've given order to kill Openbox. This will \
happen shortly after I'm done.\n"
exit

Running the script results in an error message from wait:

$ ./devel/shell/procman/procman2 && echo $?
procman2(5529): I've given order to kill Openbox. This will happen shortly after I'm done.
./devel/shell/procman/procman2: line 8: wait: pid 5529 is not a child of this shell
0

I'm still unsure why this really happens. I assume that #!/bin/bash at the beginning of a script creates a subshell in which the script is then executed. However, this is not reflected by the output of ps, as far as I can see.

#!/bin/bash
#
# procman3
cmd_name=${0##*/} # Get basename of the entered command
cmd_pid=$$ # Get PID of this script
function f_wait_exit {
while ps -p $cmd_pid > /dev/null # Check if this script is still running, but
# redirect stdout of the ps command to
# /dev/null
do
printf "Waiting for $cmd_pid to finish...\n"
sleep 1 # Don't go crazy on CPU usage
done
sleep 3 # A bit of time to read the exit code of this script after it's done
openbox --exit
}
f_wait_exit & # Execute the wait-and-exit routine in the background
printf "${cmd_name}($cmd_pid): I've given order to kill Openbox. This will \
happen shortly after I'm done.\n"
sleep 3 # Keep this script alive for 3 seconds for demonstration purposes
exit

Running this script will give the following results:

$ ./devel/shell/procman/procman3 && echo $?
procman3(4805): I've given order to kill Openbox. This will happen shortly after I'm done.
Waiting for 4805 to finish...
Waiting for 4805 to finish...
Waiting for 4805 to finish...
0

So, problem solved? I guess so, apart from the fact that using sleep in the while loop creates a race condition. But, when the script is not kept alive for demonstration, the sleep time could be brought down to half a second or less there to reduce the risk of any new process taking $cmd_pid in the meantime.