Many desktop oriented operating systems try to provide various usability improvements and features, like quite useful Expose or Dashboard in Mac OS X or useless Tiles concept in recent editions of Microsoft Windows systems.

After using UNIX for so many years I knew that I could freeze (or pause) any process in the system with kill -17 ( SIGSTOP ) signal and then unfreeze it with with kill -19 ( SIGCONT ) signal as I described in the Process Management section of the Ghost in the Shell – Part 2 article. Doing it that way for the desktop applications is PITA to say the least. Can you imagine opening xterm(1) terminal and searching for all Chromium or Firefox processes and then freezing them one by one every time you need it? Me neither.

Fortunately with introduction of so called X11 helper utilities – like xdotool(1) – it is now possible to implement it in more usable manner.

Today I will show you how to freeze any X11 application with single keyboard shortcut or mouse gesture if you utilize them in any way with small simple script.

When such feature can be useful (or what for)?

Lets say you have Firefox started with many tabs open (50+) and you know that it drains battery life from your laptop. You can close it but when You will need information from any of those tabs, then You will have to start Firefox again (even more battery usage) and load all needed tabs (battery …). The alternative is to pause all Firefox processes when You do not use them. This will freeze all its processes and subprocesses and it will not use any CPU (or battery) power. When you will need it, then you will unpause it without the need to load all tabs again.

Other example may be some heavy processing. For example you started RawTherapee or Darktable processing of large amount of photos and you are not able to smoothly watch a video. Just pause it, watch the video and unpause it again to finish its work.

Its also usable in single player gaming when You can REALLY pause the game, literally 🙂

You may want to check other articles in the FreeBSD Desktop series on the FreeBSD Desktop – Global Page where you will find links to all episodes of the series along with table of contents for each episode’s contents.

First we need to install the so called X11 helpers. Do that with this pkg(8) command.

# pkg install xprop xdotool zenity xbindkeys

Now for the script that would make all this magic happen. The desktop-pause.sh script is available on GitHub as its syntax is nicely colored there. Save it in some place where its searchable through ${PATH} variable like ~/bin or ~/script directory and make it executable.

% fetch -O ~/scripts/desktop-pause.sh https://raw.githubusercontent.com/vermaden/scripts/master/desktop-pause.sh % chmod +x ~/scripts/desktop-pause.sh % echo $PATH | grep scripts /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/home/vermaden/scripts

It has three ways of usage.

% desktop-pause.sh usage: desktop-pause.sh OPTION [ARGUMENT] OPTIONS: -a - Do pause/resume active window. -s - Do pause/resume interactively selected window. -p - Do pause/resume specified PID. -l - Do list paused processes/windows. -L - Do list paused processes/windows with PIDs. ARGUMENT: PID for '-p' option.

If started with -a option, then it would pause/unpause the currently active window. This option is best used with keyboard shortcut or mouse gesture. It you start desktop-pause.sh script with -s argument, then the cursor will change and you will be able to select which window to freeze (or unfreeze). The -p option is usable in terminal directly as you may want to freeze/unfreeze a process without X11 environment or for some debugging purposes for example. The last -l option will list applications that are currently paused.

Most present-day generation laptops have island type limited keyboards so you will have to choose for yourself which keyboard shortcut to use. As I still use 2011 ThinkPad T420s laptop with 7-row keyboard I have little more options. The [Pause Break] key seems to be the best candidate for such feature 🙂 I will use it for the ‘active window freeze/unfreeze’ with -a option and [SHIFT]-[Pause Break] key for the more interactive -s option.

To create such new keyboard shortcut we will use handy xbindkeys(1) tool.

Lets see what code we will have to put into the ~/.xbindkeysrc configuration file.

% xbindkeys --help xbindkeys 1.8.6 by Philippe Brochard usage: xbindkeys [options] where options are: -V, --version Print version and exit -d, --defaults Print a default rc file -f, --file Use an alternative rc file -p, --poll-rc Poll the rc/guile configs for updates -h, --help This help! -X, --display Set X display to use -v, --verbose More information on xbindkeys when it run -s, --show Show the actual keybinding -k, --key Identify one key pressed -mk, --multikey Identify multi key pressed -g, --geometry size and position of window open with -k|-mk option -n, --nodaemon don't start as daemon

As its single key we will need --key option. Lets do it then.

% xbindkeys --key Press combination of keys or/and click under the window. You can use one of the two lines after "NoCommand" in $HOME/.xbindkeysrc to bind a key. "(Scheme function)" m:0x0 + c:110 Pause

Now lets read the [SHIFT]-[Pause Break] sequence.

% xbindkeys --key Press combination of keys or/and click under the window. You can use one of the two lines after "NoCommand" in $HOME/.xbindkeysrc to bind a key. "(Scheme function)" m:0x1 + c:110 Shift + Pause

We now have all needed information for the ~/.xbindkeysrc configuration file. Here is how it looks configured.

% cat ~/.xbindkeysrc # [Pause Break] FOR ACTIVE WINDOW "~/scripts/desktop-pause.sh -a" Pause # [Shift]-[Pause Break] FOR INTERACTIVE WINDOW "~/scripts/desktop-pause.sh -s" Shift + Pause

Now lets start xbindkeys(1) and verify that it works.

% xbindkeys

Press the [Pause Break] key when you are in the terminal where you started xbindkeys(1) utility. Now hit [ENTER] several times, the terminal should be freezed. Now hit [Pause Break] key again. The etnered [ENTER] keys have been passed to it as it was unfreezed.

Lets check the Firefox example.

When processes run like usual they have on of the I* / S* / R* state like shown below.

% ps ax | grep firefox | grep -v grep 67981 - S 3:28.66 /usr/local/lib/firefox/firefox -contentproc -childID 58 -isForBrowser -prefsLen 31209 -schedulerPrefs 0001,2 -appdir /usr/local/lib/firefox/browser 41124 tab 41124 0- S 68:44.94 firefox 43940 0- S 25:52.43 /usr/local/lib/firefox/firefox -contentproc -childID 1 -isForBrowser -prefsLen 27620 -schedulerPrefs 0001,2 -appdir /usr/local/lib/firefox/browser 41124 tab

When you will now freeze Firefox with [Pause Break] key its processes will have T state.

% ps ax | grep firefox | grep -v grep 67981 - T 3:28.66 /usr/local/lib/firefox/firefox -contentproc -childID 58 -isForBrowser -prefsLen 31209 -schedulerPrefs 0001,2 -appdir /usr/local/lib/firefox/browser 41124 tab 41124 0- T 68:45.17 firefox 43940 0- T 25:52.85 /usr/local/lib/firefox/firefox -contentproc -childID 1 -isForBrowser -prefsLen 27620 -schedulerPrefs 0001,2 -appdir /usr/local/lib/firefox/browser 41124 tab

After you unfreeze them again with [Pause Break] key they will get back to normal I* / S* / R* state.

% ps ax | grep firefox | grep -v grep 67981 - S 3:28.67 /usr/local/lib/firefox/firefox -contentproc -childID 58 -isForBrowser -prefsLen 31209 -schedulerPrefs 0001,2 -appdir /usr/local/lib/firefox/browser 41124 tab 41124 0- S 68:45.54 firefox 43940 0- S 25:53.01 /usr/local/lib/firefox/firefox -contentproc -childID 1 -isForBrowser -prefsLen 27620 -schedulerPrefs 0001,2 -appdir /usr/local/lib/firefox/browser 41124 tab

You may of course specify by hand the Firefox PID which is 41124 in current state.

% desktop-pause.sh -p 41124 INFO: kill -17 41124 INFO: kill -17 67981 INFO: kill -17 43940

The Firefox browser will be paused again.

% ps ax | grep firefox | grep -v grep 67981 - T 3:28.68 /usr/local/lib/firefox/firefox -contentproc -childID 58 -isForBrowser -prefsLen 31209 -schedulerPrefs 0001,2 -appdir /usr/local/lib/firefox/browser 41124 tab 41124 0- T 68:46.68 firefox 43940 0- T 25:56.22 /usr/local/lib/firefox/firefox -contentproc -childID 1 -isForBrowser -prefsLen 27620 -schedulerPrefs 0001,2 -appdir /usr/local/lib/firefox/browser 41124 tab

Use it again to unpause it.

% desktop-pause.sh -p 41124 INFO: kill -19 41124 INFO: kill -19 67981 INFO: kill -19 43940

And viola! Firefox runs again.

% ps ax | grep firefox | grep -v grep 67981 - S 3:28.68 /usr/local/lib/firefox/firefox -contentproc -childID 58 -isForBrowser -prefsLen 31209 -schedulerPrefs 0001,2 -appdir /usr/local/lib/firefox/browser 41124 tab 41124 0- S 68:46.72 firefox 43940 0- S 25:56.28 /usr/local/lib/firefox/firefox -contentproc -childID 1 -isForBrowser -prefsLen 27620 -schedulerPrefs 0001,2 -appdir /usr/local/lib/firefox/browser 41124 tab

There are no downsides to this feature but one has to remember paused applications will not refresh themselves as their processes are freezed. Below you can see frozen Epiphany browser upon which the xterm(1) window was moved. Pretty Windows like effect.

After you unpause the Epiphany it gets back to normal as shown below.

Remember to add xbindkeys(1) command to your ~/.xinitrc (or ~/.xsession file) to make it permanent.

UPDATE 1

One of the Hacker News users named rhn_mk1 explained the lack of window contents refresh while application is freezed. I will just cite his comment below.

That depends on the window manager. The application state is not really affected, it just stops updating (redrawing its area). When another window moves away, the window manager asks the “underlying” application to update that area of the screen. It’s dead, so the WM keeps displaying the last thing that was there, until something else happens in that spot. On the other hand, compositing window managers will dedicate a separate buffer to each application, where they have exclusive access. That kind of a window manager would not have to ask the application to update anything – it would just take the image from the dedicated application’s buffer and update the screen with it. Since the application’s buffer can’t be modified by anything else, it would have the last state of the application in it. That would in turn find its way to the screen. No glitches.

UPDATE 2

One of the Reddit users 89luca89 pointed me to the browser-suspender solution that ‘simply suspends the browser when not in focus using STOP/CONT’ signals.

UPDATE 3

The Lobsters user seschwar pointed out that there is Stoppable Layout functionality for XMonad which automatically pauses the processes of all windows except for the active one and it also uses SIGCONT and SIGSTOP signals.

UPDATE 4

One of the Hacker News users named imglorp suggested that my “command could also iconify/minify the app’s windows”.

This is really good idea.

I just added -A and -S options that also minimize a window.

% desktop-pause.sh usage: desktop-pause.sh OPTION [ARGUMENT] OPTIONS: -a - Do pause/resume active window. -A - Do pause/resume active window and minimize it. -s - Do pause/resume interactively selected window. -S - Do pause/resume interactively selected window and minimize it. -p - Do pause/resume specified PID. -l - Do list paused processes/windows. -L - Do list paused processes/windows with PIDs. ARGUMENT: PID for '-p' option.

Here is the changelog for the desktop-pause.sh script:

https://github.com/vermaden/scripts/commit/03591a138b14cededa15a05fe9c77bf1a941795d

EOF