4

When I right click on an application's icon in the Unity launcher, I can press Quit to close all the windows corresponding to that application. Is it possible to do the same with a keyboard shortcut which operates on the active window (and all the other windows corresponding to the same application)?

I could do something similar with xkill, but in that case I do not have the message that remembers me of unsaved files.

Jacob Vlijm
  • 83,767

4 Answers4

10

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.

edwinksl
  • 23,789
Jacob Vlijm
  • 83,767
  • I need help. I have your code in my home folder. If I type python3 ~/stop_active.py in a terminal all the terminal windows are closed and I also have the message from the active windows about running processes. So the code seems to work. But if I add the same command to a shortcut key, when I press the shortcut key nothing happens. am I missing something? – user525864 Apr 02 '16 at 15:33
  • @user525864 Ah, I should have mentioned: in shortcut keys, ~ doesn't work, you need to use full paths :) – Jacob Vlijm Apr 02 '16 at 15:34
  • tanks. It works. – user525864 Apr 02 '16 at 15:44
  • I have just one question. is it really not possible to achieve it in a simpler way? without wmctrl, I mean the launcher Quit already works. is not possible to reuse it somehow? – user525864 Apr 02 '16 at 15:48
  • @user525864 I see of course what you mean, but the thing is, like in many cases, the option is coded inside the application, in this case: Unity. We then depend on the cli options an application offers. Unity does not have a cli option to close all application windows gracefully, so unfortunately, the existing code is not reusable "from outside". – Jacob Vlijm Apr 02 '16 at 15:51
  • Those simpe actions on the launcher actually use several internal commands ( which will be shown in my answer later ). That's why it's really impossible without scriting – Sergiy Kolodyazhnyy Apr 02 '16 at 23:28
1

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: 1047481448@qq.com 
# 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

Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497
  • This has a few major downsides: it blindly asks for confirmation, even if no window has unsaved changes to decide on, and -related- it asks one time for all windows, as shown in the image, while the desired decision might differ per window. Furthermore, possibly only one window might have unsaved changes and be out of sight at the moment you have to decide. Why so complicated to re- invent what existing tools already provide in a better way? – Jacob Vlijm Apr 03 '16 at 03:41
  • @JacobVlijm as far as I know , there's not a lot of tools so far to test whether or not a work has or has not been saved. That's for one. It's up to each appplication to implement this feature when it receives the closing signal. When you close LibreOffice unsaved doc, it asks. When you close a terminal window with less open it may or may not ask - depends on the term. Again, same is for cases when "desired decision might differ per window". – Sergiy Kolodyazhnyy Apr 03 '16 at 04:53
  • As for asking one time - that's the desired effect ! User specifically requested a shortcut to kill all windows of an application. They don't want to have shortcut that will prompt them window after window - they could do it manually if they wanted to, but they want automatic way to killall windows – Sergiy Kolodyazhnyy Apr 03 '16 at 04:55
  • In case of wmctrl it uses C language on the low level to implement calls to X server to "gracefully close window". Of course i can implement call to lsof , but that will take longer time – Sergiy Kolodyazhnyy Apr 03 '16 at 05:21
  • About OP's intention: "when I right click to an application in the launcher I can press Quit to close all the windows corresponding to that application. Is it possible to do the same with a keyboard shortcut..." clearly asks for a solution that only asks for confirmation if necessary, and if necessary, per window. Why would you ask for a confirmation for all windows at once? If you didn't want to close it, you didn't fire the command. The question is about not loosing data if, and only if one or more windows have unsaved changes. Exactly like right-click the icon in the launcher. – Jacob Vlijm Apr 03 '16 at 05:50
  • You're probably over interpreting the question. – Sergiy Kolodyazhnyy Apr 03 '16 at 06:00
  • It is the only way that makes sense; why would you ask for confirmation on a decision you already made? You'd want the action to detect the need for confirmation, like Ctrl+Q does. Or right- click the icon in Unity. – Jacob Vlijm Apr 03 '16 at 06:06
  • This does not do what the Quit in the launcher does. If say, I have many windows in, possibly, different workspaces and I want to close all them I do not want to have to look for the file that I forgot to save. The Quit in the launcher remembers me that a specific file is not saved – user525864 Apr 03 '16 at 12:27
  • I like the idea of using only pre-installed tools, but it doesn't do the job. – user525864 Apr 03 '16 at 13:00
1

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]])
Jae
  • 126
-2

You could create a shortcut for xkill.

settings < keyboard < shortcuts < custom shortcuts

Add a custom shortcut, then simply write "xkill" in the command menu and set your own shortcut by pressing the keys you want.

Like you said that doesn't give you a "saved unsaved files" message though.

nebulon
  • 65