Hi,
I've been experimenting for some time now and finally I put myself together to tell what I have found out.
I would call the situation an ordered mess. Mess, because we all know how the situation looks like, ordered, because it's not completely bad and could improve in the future.
The biggest problem is that some (or many) applications can not be closed externally. Basically there are two categories:
1. Applications that do not support external closure,
2. Applications that support external closure.
Category 1. is a bad thing (tm), because you need a lot of trickery to close it cleanly and it might sometimes fail.
Thunderbird is an example of the application that falls into this category. It cannot be cleanly closed neither by a signal, nor by
dbus, not even by command line. To deal with the
Thunderbird clean close I use
xdotool sending keystrokes, but it does not always work (well, works most of the time). Another examples of the applications that I use and they don't support external closure are:
FPM2 (Figaro's Password Manager 2) and
Osmo (personal organizer). For those two applications I have modified their source code to catch
SIGTERM and exit cleanly.
Category 2. is a much nicer one. Basically I discovered 2 possibilities (a, b) and I can think of a third one (c):
a) application supports
dbus and registers with the session manager,
b) application supports catching signals and clean closure,
c) application supports command line closure of a running instance(s).
Possibilities b) and c) are the most cumbersome here. You have to track the PID of the application and send it a signal when you want to close it (I'm not entirely sure if the signal is automatically sent when the X server is closed) and the application has no possibility of preventing the X server (session) shutdown, which is the whole point here. The example of application I use is
Liferea (feed reader).
Possibility a) is the most promising one (actually it works as expected). When this kind of application starts, it registers itself with the session manager (over
dbus) and then it is able to receive a signal when a user requests
Log out. Then, the application can reply to the session manager that it has unsaved document and prevent the session from being shutdown if the user chooses so. Disclaimer: I'm not entirely sure about this mechanism, but I guess it is something like that. Example of an application that falls here is
Kate (KDE's text editor).
My solution as of now for the applications that do not register with the session manager over
dbus is the following one (I'm using
Xfce):
1) I have overridden
xfce4-session-logout with my own script, which is invoked whenever the user wants to log out or shutdown,
2) the script presents the user with a dialogue of confirmation,
3) if the user confirms the requested action, the script checks if there are some interesting applications running and grabs theirs PIDs,
4) the script tries to close the applications using per-application trickery,
5) if the application is not closed, the script spins over the PID preventing the session from being closed, which forces the user to close the specified application manually.
The script is as follows:
Code:
#!/bin/sh
ACTION=
PID_TERM=
CMD_WCLO=
PID_WCLO=
##
PID_TERM[0]=$(pgrep -d' ' -f -U$UID "^/usr/bin/osmo")
PID_TERM[1]=$(pgrep -d' ' -f -U$UID "^/usr/bin/liferea")
PID_TERM[2]=$(pgrep -d' ' -f -U$UID "^/usr/bin/fpm2")
PID_TERM[3]=$(pgrep -d' ' -f -U$UID "xfce4-notes")
##
CMD_WCLO[0]="thunderbird"
PID_WCLO[0]=$(pgrep -d' ' -U$UID "^${CMD_WCLO[0]}$")
echo "PID TERM: ${PID_TERM[@]}"
echo "PID WCLO: ${PID_WCLO[@]}"
################################################################################
function logout_dialog() {
################################################################################
local ACTION=
ACTION=$(kdialog --title "Log out" --default "Log out" --combobox "Choose action:" "Log out" "Restart" "Shutdown")
[ -z "$ACTION" ] && return
case "$ACTION" in
"Log out") ACTION="--logout" ;;
"Restart") ACTION="--reboot" ;;
"Shutdown") ACTION="--halt" ;;
*) ACTION= ;;
esac
echo $ACTION
}
################################################################################
function close_window() {
################################################################################
local PID=$1
local CMD=$2
local WINDOWID=
[ -n "$CMD" ] && "$CMD"
WINDOWID=$(xdotool search --pid $PID)
for WIN in $WINDOWID; do
if xprop -id $WIN | grep _NET_WM_ACTION_CLOSE; then
xdotool windowactivate --sync $WIN
xdotool key --clearmodifiers --window $WIN ctrl+q
break
fi
done
}
ACTION=$(logout_dialog)
[ -z "$ACTION" ] && exit 0
for PID in ${PID_TERM[@]}; do
kill -TERM $PID
done
for ((idx = 0; idx < ${#PID_WCLO[@]}; idx++)); do
[ -n "${PID_WCLO[$idx]}" ] && close_window ${PID_WCLO[$idx]} ${CMD_WCLO[$idx]}
done
for PID in ${PID_TERM[@]} ${PID_WCLO[@]}; do
while kill -0 $PID &>/dev/null; do
echo "PID WAIT: $PID"
sleep 1
done
done
/usr/bin/xfce4-session-logout -f $ACTION
And finally I'd like to ask for some help: I've been looking for an example on how to an application can register with the session manager over
dbus and interact with it in order to prevent the shutdown (like in the case of
Kate) but couldn't find anything interesting. I'm sure it must be some generic solution, because from my experience it works the same way both in
KDE and
Xfce. If anybody knows something, please let me know.
--
Best regards,
Andrzej Telszewski