How can I make a shortcut to close all the windows of the same application?

Solution 1:

How to safely close all windows of a gui application with a shortcut key

The safest way to close an application's window gracefully, and make sure you will not lose data, is using wmctrl (not installed by default):

wmctrl -ic <window_id>

To use it in a script to close all windows of an application:

  1. install both xdotool and wmctrl

    sudo apt-get install wmctrl xdotool
    
  2. Copy the script below into an empty file, save it as stop_active.py

    #!/usr/bin/env python3
    import subprocess
    
    def get(cmd):
        return subprocess.check_output(cmd).decode("utf-8").strip()
    
    pid = get(["xdotool", "getactivewindow", "getwindowpid"])
    for w in get(["wmctrl", "-lp"]).splitlines():
        if pid in w and not "Desktop" in w:
            subprocess.call(["wmctrl", "-ic", w.split()[0]])
    
  3. Add the following command to a shortcut key:

    python3 /path/to/stop_active.py
    

    Choose: System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts". Click the "+" and add the command:

    python3 /path/to/stop_active.py
    

    N.B. don't use ~ or $HOME in a shortcut key, but use absolute paths instead.

Now the shortcut can be used to gracefully close all windows of the frontmost window.

Explanation

I tried the various kill options (kill -2, kill -HUP, kill -s TERM <pid> etc), which are mentioned in several posts to close an application gracefully. Tested on a gedit window with unsaved changes, all of them closed the window happily however, without any interaction.

wmctrl does ask what to do however, similar to Ctrl+Q.

The script then first finds out the pid of the frontmost window, with the command:

xdotool getactivewindow getwindowpid

subsequently, the list of currently opened windows is called with the command:

wmctrl -lp

From this list, the corresponding windows are picked and closed with the command:

wmctrl -ic <window_id>

Note

If you are closing all nautilus windows, in the line

if pid in w and not "Desktop" in w:

"Desktop" is referring to your Desktop window, which normally should always stay. If you are not using an English version of Ubuntu, replace "Desktop" by the localized name of the Desktop in your language.

Solution 2:

The answer by user72216 didn't work always. For example if I opened several Okular (a KDE PDF viewer) windows, the code won't close all windows, as different window ids are assigned to the windows. The following code extracts all window ids and closes them gracefully:

#!/usr/bin/env python3
import subprocess

def get(cmd):
    return subprocess.check_output(cmd).decode("utf-8").strip()

pid = get(["xdotool", "getactivewindow", "getwindowpid"])

# Identify the name of the application
username = get(["users"])
jobs = get(["ps","-u",username])
jobname = ""
for w in jobs.splitlines():
    jobinfo = w.split()
    if pid == jobinfo[0]:
        jobname = jobinfo[-1]
        break

# Get all pids that match the jobname
pidlist = []
for w in jobs.splitlines():
    jobinfo = w.split()
    if jobinfo[-1] == jobname:
        pidlist = pidlist + [jobinfo[0]]

# Close all windows with having the pids
wlist = get(["wmctrl", "-lp"])
for pid in pidlist:
    for w in wlist.splitlines():
        if pid in w and not "Desktop" in w:
            print(w.split()[0])
            subprocess.call(["wmctrl", "-ic", w.split()[0]])

Solution 3:

Introduction

The script bellow kills all active windows of the currently active window that user is working in. This is meant to be bound to a shortcut.

The script will show a popup prompting the user to confirm before killing all the windows.

The script uses all the native (pre-installed ) tools, such as qdbus,zenity, and bash.

Obtaining the script

You can either copy the script source here or obtain it from my git repository using instructions bellow

  1. sudo apt-get install git
  2. cd /opt ; sudo git clone https://github.com/SergKolo/sergrep.git
  3. The file will be located in /opt/sergrep/kill_windows_set.sh ; Ensure the file is executable with sudo chmod +x kill_windows_set.sh

Binding the script to shortcut

The relevant information can be found here:

How do I bind .sh files to keyboard combination?

Source

#!/usr/bin/env bash
#
###########################################################
# Author: Serg Kolo , contact: [email protected] 
# Date: April 2nd , 2016
# Purpose:  Close all windows of the active application
# Written for: https://askubuntu.com/q/753033/295286
# Tested on: Ubuntu 14.04 LTS
###########################################################
# Copyright: Serg Kolo , 2016
#    
#     Permission to use, copy, modify, and distribute this software is hereby granted
#     without fee, provided that  the copyright notice above and this permission statement
#     appear in all copies.
#
#     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
#     THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
#     DEALINGS IN THE SOFTWARE.

ARGV0="$0"
ARGC=$#
get_running_apps()
{
  qdbus org.ayatana.bamf /org/ayatana/bamf/matcher org.ayatana.bamf.matcher.RunningApplications
}

list_children()
{
 qdbus org.ayatana.bamf "$1"  org.ayatana.bamf.view.Children
}

get_pid()
{
 qdbus org.ayatana.bamf "$1"  org.ayatana.bamf.window.GetPid
}

main()
{
  local ACTIVE
  local apps_list
  apps_list=( $( get_running_apps | tr '\n' ' ' ) )

  for app in ${apps_list[@]} ; do
      ACTIVE=$(qdbus org.ayatana.bamf $app org.ayatana.bamf.view.IsActive)
      if [ "x$ACTIVE" = "xtrue"   ] ; then
         windows=( $( list_children $app | tr '\n' ' ' ) )
      fi
  done

for window in ${windows[@]} ; do
    PIDS+=( $(get_pid $window) )
done

if zenity --question \
   --text="Do you really want to kill ${#PIDS[@]} windows ?" ; 
   then
   kill ${PIDS[@]}
fi

}
main

Script in Action

enter image description here