close-all-windows 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #!/bin/bash
  2. me="$(basename "$0")"
  3. ##########################################################################
  4. # USER CONFIGURATION
  5. # please fill this space-separated array to your liking.
  6. # each word stands for an application's class or classname that will be
  7. # searched for, case insensitively, in xwininfo's output.
  8. # please check with:
  9. # xprop | grep -E '^WM_CLASS'
  10. # or
  11. # xwininfo -root -tree | grep -iE '(.*myapp.*)' where myapp is one
  12. # of the words to be added.
  13. excludeapps=( tint2 conky xfce4-panel glava )
  14. # array of commands (or functions) to execute before cycling through all windows
  15. executes=( borgbackup-off transmission-off "audtool --mainwin-show off" amule-off )
  16. # if this string is non-empty it denotes a filename to store debug info in.
  17. #~ debug_log="$HOME/${me}.$(date +%s).debug"
  18. debug_log=""
  19. ###########################################################################
  20. #~ identifier=$(date +%s)
  21. #~ identifier="urxvt${identifier%??}"
  22. # the close-all-windows-wrapper is using this same identifier as window class.
  23. #~ excludeapps_winid[j]="$(echo "ibase=10;obase=16;$WINDOWID"|bc)" # the window we're currently in
  24. # window id to hex. variations:
  25. # - xwininfo seems to not use leading zeroes, but a leading 0x
  26. # - wmctrl seems to always pad to 8 digits + a leading 0x
  27. #~ self="$(printf "0x%08x" "$WINDOWID")"
  28. ############################################################################
  29. # FUNCTIONS
  30. debug() {
  31. [ -w "$debug_log" ] && echo -e "$@" >> "$debug_log"
  32. }
  33. amule-off() {
  34. while pidof -x amule >/dev/null 2>&1; do
  35. echo "Waiting for amule to shut down"
  36. sleep 1
  37. done
  38. }
  39. transmission-off() {
  40. if pidof transmission-daemon >/dev/null 2>&1; then
  41. read -n1 -p "Do you want to close transmission-daemon [Y/n]? " answer
  42. [[ "$answer" =~ [Nn] ]] && return
  43. transmission-remote --authenv --exit
  44. echo "Waiting for transmission-daemon to shut down"
  45. while pidof transmission-daemon >/dev/null 2>&1; do
  46. sleep 0.1
  47. echo -n "."
  48. done
  49. echo " Success!"
  50. fi
  51. }
  52. borgbackup-off(){
  53. borgdelay=10
  54. if pidof -x borgbackup.sh >/dev/null 2>&1; then
  55. echo "Cannot exit while borgbackup.sh is running!"
  56. while pidof -x borgbackup.sh >/dev/null 2>&1; do
  57. echo "Checking again in $borgdelay seconds."
  58. sleep $borgdelay
  59. done
  60. while pidof -x borgbackup.sh >/dev/null 2>&1; do
  61. echo "Checking again in $borgdelay seconds."
  62. sleep $borgdelay
  63. done
  64. echo "Finished!"
  65. # Because the above might've taken a very long time,
  66. # wiggle the mouse to disable screensaver:
  67. xdotool mousemove_relative --sync -p 0 100; sleep 0.1; xdotool mousemove_relative --sync -p 180 100
  68. fi
  69. }
  70. ############################################################################
  71. # MAIN
  72. # simple dependency check
  73. for dep in grep xwininfo wmctrl; do
  74. type -f $dep >/dev/null || exit 1
  75. done
  76. sep="$(stty size)"
  77. sep="${sep#* }"
  78. ((sep > 10 )) && sep="$(for (( ; sep > 0 ; sep-- )); do printf "-"; done)" || sep="----------"
  79. echo "Gracefully closing all windows."
  80. debug "Debugging $me on $(date)\n$sep\n$(wmctrl -m 2>&1)\n$sep$(wmctrl -l 2>&1)\n$sep\n$(wmctrl -d 2>&1)\n$sep"
  81. for cmd in "${executes[@]}"; do
  82. echo -e "$sep\nExecuting: $cmd"
  83. $cmd
  84. done
  85. excludeapps_winid=()
  86. for (( i=0,j=0 ; i<${#excludeapps[@]} ; i++ ))
  87. do
  88. debug "checking for app $i - ${excludeapps[i]}:"
  89. while read excludeapps_winid[j]
  90. do
  91. debug "Matching output line $j - ${excludeapps_winid[j]}"
  92. if [[ "x${excludeapps_winid[j]}" != "x" ]]
  93. then
  94. debug "Is on the list of excludeapps."
  95. excludeapps_winid[j]="${excludeapps_winid[j]%% *}"
  96. debug "Exclude app $j: ${excludeapps_winid[j]}"
  97. j=$((j + 1))
  98. else
  99. debug "Got an empty string :-("
  100. fi
  101. done <<< "$(xwininfo -root -tree -int | grep -i "(.*${excludeapps[i]}.*)")"
  102. # dump all window info, with IDs in decimal, and grep for classes/names,
  103. # in brackets like: ("urxvt" "URxvt")
  104. done
  105. # Add this window to the array of excluded apps
  106. # WINDOWID uses the same decimal format, just append it to the array
  107. excludeapps_winid=( ${excludeapps_winid[@]} $WINDOWID )
  108. echo "$sep
  109. List of classes/names to exclude: \"${excludeapps[@]}\" and self.
  110. $sep
  111. Window IDs to exclude: ${excludeapps_winid[@]}"
  112. debug "$sep\nMatching 'wmctrl -l' output against excludeapps winids:"
  113. while read -r line
  114. do
  115. debug "$sep\nLine $i - $line"
  116. for (( i=0 ; i<${#excludeapps_winid[@]} ; i++ ))
  117. do
  118. excludeapps_winid[i]="0x$(printf '%08x' ${excludeapps_winid[i]})"
  119. debug "${line%% *} = ${excludeapps_winid[i]} - are they the same?"
  120. if [[ "${line%% *}" == "${excludeapps_winid[i]}" ]]
  121. then
  122. debug "Yes they are! Skipping!"
  123. line=''
  124. break
  125. else
  126. debug "No they aren't."
  127. fi
  128. done
  129. if [[ "x$line" != "x" ]]
  130. then
  131. echo -e "$sep\nClosing: ${line/$HOSTNAME/}"
  132. # go to the desktop in question...
  133. wmctrl -s "${line:10:3}"
  134. # ...and close:
  135. wmctrl -ic "${line%% *}"
  136. # checking if it's really closed now:
  137. if wmctrl -l | grep -E "^${line%% *}" >/dev/null 2>&1
  138. then
  139. debug "${line%% *} hasn't shut down yet. waiting..."
  140. # wait, presumably for user action:
  141. echo -n "Waiting . "
  142. while wmctrl -l | grep -E "^${line%% *}" >/dev/null 2>&1
  143. do
  144. sleep 0.1; echo -n '.'
  145. done
  146. echo
  147. debug "${line%% *} has finally shut down!"
  148. fi
  149. fi
  150. done <<< "$(wmctrl -l)"
  151. debug "$sep\nComing back to $me - winid $WINDOWID"
  152. wmctrl -ia "0x$(printf '%08x' $WINDOWID)" >/dev/null 2>&1
  153. debug "$sep\nThat's it - see what's left:\n$(wmctrl -m 2>&1)\n$sep
  154. $(wmctrl -l 2>&1)\n$sep\n$(wmctrl -d 2>&1)\n$sep"
  155. echo "$sep
  156. Press <Enter> for \"$@\" or anything else to continue."
  157. oldifs="$IFS"
  158. IFS=
  159. read -s -n 1 key
  160. # -s: do not echo input character. -n 1: read only 1 character
  161. if [ -z "$key" ]; then
  162. IFS="$oldifs"
  163. debug "Doing \"$@\""
  164. while :
  165. do
  166. $@
  167. sleep 2
  168. done
  169. else
  170. IFS="$oldifs"
  171. debug "Doing Nothing"
  172. fi