close-all-windows 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  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 )
  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. transmission-remote --authenv --exit
  42. echo "Waiting for transmission-daemon to shut down"
  43. while pidof transmission-daemon >/dev/null 2>&1; do
  44. sleep 0.1
  45. echo -n "."
  46. done
  47. echo " Success!"
  48. fi
  49. }
  50. borgbackup-off(){
  51. borgdelay=10
  52. if pidof -x borgbackup.sh >/dev/null 2>&1; then
  53. echo "Cannot exit while borgbackup.sh is running!"
  54. while pidof -x borgbackup.sh >/dev/null 2>&1; do
  55. echo "Checking again in $borgdelay seconds."
  56. sleep $borgdelay
  57. done
  58. echo "Finished!"
  59. # Because the above might've taken a very long time,
  60. # wiggle the mouse to disable screensaver:
  61. xdotool mousemove_relative --sync -p 0 100; sleep 0.1; xdotool mousemove_relative --sync -p 180 100
  62. fi
  63. }
  64. ############################################################################
  65. # MAIN
  66. # simple dependency check
  67. for dep in grep xwininfo wmctrl; do
  68. type -f $dep >/dev/null || exit 1
  69. done
  70. sep="$(stty size)"
  71. sep="${sep#* }"
  72. ((sep > 10 )) && sep="$(for (( ; sep > 0 ; sep-- )); do printf "-"; done)" || sep="----------"
  73. echo "Gracefully closing all windows."
  74. 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"
  75. for cmd in "${executes[@]}"; do
  76. echo -e "$sep\nExecuting: $cmd"
  77. $cmd
  78. done
  79. excludeapps_winid=()
  80. for (( i=0,j=0 ; i<${#excludeapps[@]} ; i++ ))
  81. do
  82. debug "checking for app $i - ${excludeapps[i]}:"
  83. while read excludeapps_winid[j]
  84. do
  85. debug "Matching output line $j - ${excludeapps_winid[j]}"
  86. if [[ "x${excludeapps_winid[j]}" != "x" ]]
  87. then
  88. debug "Is on the list of excludeapps."
  89. excludeapps_winid[j]="${excludeapps_winid[j]%% *}"
  90. debug "Exclude app $j: ${excludeapps_winid[j]}"
  91. j=$((j + 1))
  92. else
  93. debug "Got an empty string :-("
  94. fi
  95. done <<< "$(xwininfo -root -tree -int | grep -i "(.*${excludeapps[i]}.*)")"
  96. # dump all window info, with IDs in decimal, and grep for classes/names,
  97. # in brackets like: ("urxvt" "URxvt")
  98. done
  99. # Add this window to the array of excluded apps
  100. # WINDOWID uses the same decimal format, just append it to the array
  101. excludeapps_winid=( ${excludeapps_winid[@]} $WINDOWID )
  102. echo "$sep
  103. List of classes/names to exclude: \"${excludeapps[@]}\" and self.
  104. $sep
  105. Window IDs to exclude: ${excludeapps_winid[@]}"
  106. debug "$sep\nMatching 'wmctrl -l' output against excludeapps winids:"
  107. while read -r line
  108. do
  109. debug "$sep\nLine $i - $line"
  110. for (( i=0 ; i<${#excludeapps_winid[@]} ; i++ ))
  111. do
  112. excludeapps_winid[i]="0x$(printf '%08x' ${excludeapps_winid[i]})"
  113. debug "${line%% *} = ${excludeapps_winid[i]} - are they the same?"
  114. if [[ "${line%% *}" == "${excludeapps_winid[i]}" ]]
  115. then
  116. debug "Yes they are! Skipping!"
  117. line=''
  118. break
  119. else
  120. debug "No they aren't."
  121. fi
  122. done
  123. if [[ "x$line" != "x" ]]
  124. then
  125. echo -e "$sep\nClosing: ${line/$HOSTNAME/}"
  126. # go to the desktop in question...
  127. wmctrl -s "${line:10:3}"
  128. # ...and close:
  129. wmctrl -ic "${line%% *}"
  130. # checking if it's really closed now:
  131. if wmctrl -l | grep -E "^${line%% *}" >/dev/null 2>&1
  132. then
  133. debug "${line%% *} hasn't shut down yet. waiting..."
  134. # wait, presumably for user action:
  135. echo -n "Waiting . "
  136. while wmctrl -l | grep -E "^${line%% *}" >/dev/null 2>&1
  137. do
  138. sleep 0.1; echo -n '.'
  139. done
  140. echo
  141. debug "${line%% *} has finally shut down!"
  142. fi
  143. fi
  144. done <<< "$(wmctrl -l)"
  145. debug "$sep\nComing back to $me - winid $WINDOWID"
  146. wmctrl -ia "0x$(printf '%08x' $WINDOWID)" >/dev/null 2>&1
  147. debug "$sep\nThat's it - see what's left:\n$(wmctrl -m 2>&1)\n$sep
  148. $(wmctrl -l 2>&1)\n$sep\n$(wmctrl -d 2>&1)\n$sep"
  149. echo "$sep
  150. Press <Enter> for \"$@\" or anything else to continue."
  151. oldifs="$IFS"
  152. IFS=
  153. read -s -n 1 key
  154. # -s: do not echo input character. -n 1: read only 1 character
  155. if [ -z "$key" ]; then
  156. IFS="$oldifs"
  157. debug "Doing \"$@\""
  158. while :
  159. do
  160. $@
  161. sleep 2
  162. done
  163. else
  164. IFS="$oldifs"
  165. debug "Doing Nothing"
  166. fi