Wed, 31 Dec 2008

Taming the fox (part 2)

So, in my previous post I wrote a script that uses xdotool to put firefox to sleep when it does not have focus. The implementation was pretty straight-forward, but not optimal. Thanks to some help from Jordan Sissel, I threw together a Python script that accomplishes this goal much more effectively, using the python-xlib module instead of xdotool.
import os
from signal import SIGSTOP, SIGCONT
from Xlib import X, display, Xatom

def watch(properties):
    """ A generator that yields events for a list of X properties """
    dpy = display.Display()
    screens = dpy.screen_count()
    atoms = {}
    wm_pid = dpy.get_atom('_NET_WM_PID')

    for property in properties:
        atomid = dpy.get_atom(property, only_if_exists=True)
        if atomid != X.NONE:
            atoms[atomid] = property

    for num in range(screens):
        screen = dpy.screen(num)

    while True:
        ev = dpy.next_event()
        if ev.type == X.PropertyNotify:
            if ev.atom in atoms:
                data = ev.window.get_full_property(ev.atom, 0)
                id = int(data.value.tolist()[0])
                window = dpy.create_resource_object('window', id)
                if == 0: continue
                pid = int(window.get_full_property(wm_pid, 0).value.tolist()[0])
                title = window.get_full_property(Xatom.WM_NAME, 0).value
                yield atoms[ev.atom], title, pid, data

def tamefox():
    """ Puts firefox to sleep when it loses focus """
    alive = True
    ff_pid = None
    for property, title, pid, event in watch(['_NET_ACTIVE_WINDOW']):
        if title.endswith('Firefox') or title.endswith('Vimperator'):
            ff_pid = pid
            if not alive:
                print 'Waking up firefox'
                os.kill(ff_pid, SIGCONT)
                alive = True
        elif ff_pid and alive and not title.startswith('Opening') and \
                title not in ('Authentication Required', 'Confirm', 'Alert'):
            print 'Putting firefox to sleep'
            os.kill(ff_pid, SIGSTOP)
            alive = False

if __name__ == '__main__':

posted at: 07:03 | link | Tags: , | 9 comments

Posted by Colin Walters at Wed Dec 31 08:31:13 2008

Cool hack.  It's really something that should be part of Firefox itself, though doing it inside the firefox process would be quite tricky though.

Of course, you probably want to stop Flash npviewer.bin processes too.

Random general note: if you are an application author and want to do some sort of heavy lifting only when you're the foreground application, connecting to the "notify::active" in a Gtk.Window will do the right thing.  I use this in HotSSH to enable/disable connection status polling.

Posted by Luke at Wed Dec 31 08:52:38 2008

@Colin: My F10-x86_64 setup no longer uses the npviewer.bin, as flash seems to be native now -- but yeah, the script could easily be modified to SIGSTOP that as well.

Posted by Peter at Wed Dec 31 10:44:04 2008

It is not such a good idea, let alone having it in firefox itself.
In those modern web applications, you know, there is this javascript running that makes bunch of stuff, one being making XMLHTTPreuqests to the server on sites requiring you being logged in in order to work. Even worse, real time applications like meebo keep the http1.1 connection open at all time, so the result after 5 minutes ff being not the active window will be you being logged out.
On the other side if all those open tabs and windows do not require the application to be really running this will also mean they do not require to be loaded, i solved my problem with many many open tabs by using "read it later" extension and I just mark the stuff i want to keep handy and then close it.

Posted by Luke at Wed Dec 31 16:43:54 2008

Yes, Peter, this will not play well with "real time" web applications, as persistent XMLHttpRequest's would most likely timeout.  Your average person does not, and should not care about what Firefox is doing in the background.  This hack is merely for geeks who want to save power or CPU cycles under certain circumstances.  Such people should also understand the implications of such actions.

I don't think this behavior should be the default in Firefox either, as I am a big fan of live web applications, and I think they are going to become even more prevalent in the future.

Thanks for the "read it later" tip, I'll take a look at that Firefox extension...

Posted by Luke at Wed Dec 31 21:20:12 2008

Also, another caveat of this approach:  It will send firefox a SIGSTOP when a password dialog pops up.  You can resolve this by running 'kill -CONT `pidof firefox`'.  Workaround suggestions welcome :)

Posted by Saikat Guha at Wed Dec 31 21:36:21 2008

Interesting hack. One problem though is with the script getting confused when there is a local firefox copy running as well as a remote firefox through X-forwarding. It tries to SIGSTOP/SIGCONT the remote pid, and crashes out when os.kill throws an exception.

Posted by Cindy at Sun Jun 12 19:59:58 2011

Hey, you’re the goto expert. Tnhaks for hanging out here.

Posted by Debra at Mon Jun 13 15:51:04 2011

Your answer was just what I ndeeed. It’s made my day!

Posted by Walter at Fri Dec 5 08:28:00 2014

I will be coming up this weenekd with the Stuart Fly Rodders. I would like to know if you would have a guide available for a half day on either Friday  late morning or Saturday for a 1/2 day of fishing and if so what would be the price?Thank YouJohn R