Custom right-click action in Unity Launcher keeps cursor busy for 20 seconds

To have a "minimize window" option through right click on an icon in the Unity launcher (Ubuntu 14.04), I followed the detailed instructions here about changing a .desktop file and made a custom Firefox launcher in ~/.local/share/applications/ folder. The relevant part of the .desktop file is:

Actions=NewWindow;NewPrivateWindow;Minimize

[Desktop Action NewWindow]
Name=Open a New Window
Exec=firefox -new-window
OnlyShowIn=Unity;

[Desktop Action NewPrivateWindow]
Name=Open a New Private Window
Exec=firefox -private-window
OnlyShowIn=Unity;

[Desktop Action Minimize]
Name=Minimize Windows
Exec=sh /home/myusername/Documents/xdotool_sh/minimize.sh firefox
OnlyShowIn=Unity;

The desktop action "Minimize" invokes a simple shell script, minimize.sh that has the following content:

#/bin/bash
name=$1
for i in $(xdotool search --class "$name"); do
    xdotool windowminimize $i
done

The script uses xdotool, that can be installed from the official repositories, to find all the firefox windows, iterate over them and minimize them.

The script works and the launcher right menu option "Minimize Windows" works as well, but as soon as the windows are minimized, the mouse pointer gets in "busy" mode and stays like this for about 20 seconds (although the mouse actions are still responsive).

Does anyone know why starting a shell script from a right menu option in Unity could lead to this behaviour?

EDIT: Apparently the wait period is inevitable, as explained in Jacob Vlijm's answer. Since the mouse remains responsive, avoiding the transformation of the pointer in the spinning wheel is a partial esthetic workaround, as explained on askubuntu.

EDIT2: giving the system a fake window is a better solution, as explained by Jacob below.


Excellent question.

The cause

Normally, when starting GUI applications from the Unity Launcher, the launcher waits for a window to appear. While waiting, it shows the "spinning wheel". It won't wait forever however; after about 20 seconds, the launcher assumes the window won't appear and gives up waiting.

1. The main command of an application's launcher

In a .desktop file, concerning the first Exec= line (the main command), you can tell the launcher to wait or not, in a line:

StartupNotify=true

to make it wait, or

StartupNotify=false

to make it not wait.

2. Quicklist items of a launcher

For possible quicklist (right-click) items of a launcher however, the default value is StartupNotify=true. Unfortunately, this value is fixed and cannot be changed by anything.

That means that if you start any command from right-clicking a launcher icon in the Unity Launcher, the launcher is expecting a window and waits for it, showing the spinning wheel.

The bottom line is that, although it can be explained, there seems to be no solution to the issue at the moment, other then creating a dedicated launcher for your script and add the line StartupNotify=false to the file.

The proof

You can test the behaviour yourself. Create two launchers:

[Desktop Entry]
Name=Test
Exec=sh /home/myusername/Documents/xdotool_sh/minimize.sh firefox
Type=Application
StartupNotify=true

and:

[Desktop Entry]
Name=Test
Exec=sh /home/myusername/Documents/xdotool_sh/minimize.sh firefox
Type=Application
StartupNotify=false

Save them as test1.desktop and test2.desktop, drag both launchers on to the Unity launcher. Click them and see the difference in behaviour.


Edit; if it really bothers you, feed Unity a fake window

If you have many scripts in quicklists, and/or it really bothers you, there is another cosmetic solution; we can fake, invisible (fully transparent) to show a window, to be included in your script. your script would then be (e.g.)

#/bin/bash
name=$1
for i in $(xdotool search --class "$name"); do
    xdotool windowminimize $i
done
fake_window

where the command fake_window will call our (fake-) window, making Unity end the spinning wheel.

How to set up

  1. Create, if it does not exist yet, the directory ~/bin
  2. Copy the script below into an empty file, save it as fake_window (no extension) in ~/bin and make it executable

    #!/usr/bin/env python3
    from gi.repository import Gtk
    from threading import Thread
    import time
    import subprocess
    
    """
    This is a self-destroying window, to "feed" Unity a fake-window, preventing
    the launcher to show a spinning wheel, waiting for a window to appear.
    Include the command to run this script at the end of the (your) script.
    """
    
    class FakeWin(Gtk.Window):
    
        def __init__(self):
            Gtk.Window.__init__(self, title="1526closeme")
            Thread(target = self.close).start()
    
        def close(self):
            t = 0
            while t < 150:
                time.sleep(0.2)
                try:
                    pid = subprocess.check_output(["pgrep", "-f", "fake_window"])\
                          .decode("utf-8").strip()
                    subprocess.Popen(["kill", pid])
                    break
                except subprocess.CalledProcessError:
                    pass
                t += 1
    
    def fakewindow():
        window = FakeWin()
        # make our window transparent
        window.set_opacity(0)
        window.set_default_size(0,0)
        window.show_all()
        Gtk.main()
    
    fakewindow()
    
  3. Add to the very end of your script the command:

    fake_window
    
  4. Log out and back in (or run source ~/.profile)

That's it, the wheel will now only spin for as long as the script runs.

Explanation

The script does create a minimalistic window. The window however is fully transparent and has a size of 0x0 pixels and is thus invisible. It will destroy itself instantly once it exists.

Calling the window at the end of your script, you will satisfy Unity's wish for a window, stopping the wheel to spin.


Just wanted to add an alternate fake_window implementation that is a little bit simpler and doesn't trigger python warnings on an Ubuntu 16.04 system.

#!/usr/bin/env python3

import gi
gi.require_version('Gtk', '3.0')

from gi.repository import Gtk
from gi.repository import GLib

"""
This is a self-destroying window, to "feed" Unity a fake-window, preventing
the launcher to show a spinning wheel, waiting for a window to appear.
Include the command to run this script at the end of the (your) script.
"""

def timer_cb():
    Gtk.main_quit()
    return False

def show_cb(widget, data=None):
    GLib.timeout_add(500, timer_cb)

def fakewindow():
    window = Gtk.Window()
    # make our window transparent
    window.set_opacity(0)
    window.set_default_size(0,0)

    window.connect("show", show_cb)

    window.show_all()

    Gtk.main()

fakewindow()