A Python screensaver for xscreensaver (Linux)

I know remarkably little about Python and GTK, but from the two URLs shown in the source code below I was able to piece together a working, “Hello, world” Linux screensaver. Well, calling it a screensaver is a stretch, because what it will really do is burn the characters “Hello, world” into your monitor; but at least I cracked the code on how to get this started.

To try this on your own Linux system running xscreensaver, first save the following source code somewhere. I’ll assume that you’ve saved it to /home/al/hello.py:

#!/usr/bin/python

import os
import sys

import gtk
from gtk import gdk

# the secret sauce is to get the "window id" out of $XSCREENSAVER_WINDOW
# code comes from these two places:
# 1) http://pastebin.com/nSCiq1P3
# 2) http://stackoverflow.com/questions/4598581/python-clutter-set-display

class ScreenSaverWindow(gtk.Window):

    def __init__(self):
        gtk.Window.__init__(self)
        pass

    def realize(self):
        if self.flags() & gtk.REALIZED:
            return

        ident = os.environ.get('XSCREENSAVER_WINDOW')
        if not ident is None:
            print 'if not ident is None:'
            self.window = gtk.gdk.window_foreign_new(int(ident, 16))
            self.window.set_events (gdk.EXPOSURE_MASK | gdk.STRUCTURE_MASK)
            # added by aja
            x, y, w, h, depth = self.window.get_geometry()
            self.size_allocate(gtk.gdk.Rectangle(x, y, w, h))
            self.set_default_size(w, h)
            self.set_decorated(False)
            # aja - more
            self.window.set_user_data(self)
            self.style.attach(self.window)
            self.set_flags(self.flags() | gtk.REALIZED)
            #self.window.connect("destroy", self.destroy)

        if self.window == None:
            print 'self.window == None:'
            self.window = gdk.Window(None, 1024, 768, gdk.WINDOW_TOPLEVEL,
                                     (gdk.EXPOSURE_MASK | gdk.STRUCTURE_MASK),
                                     gdk.INPUT_OUTPUT)

        if self.window != None:
            print 'self.window != None:'
            #self.window.add_filter(lambda *args: self.filter_event(args))
            self.set_flags(self.flags() | gtk.REALIZED)

window = ScreenSaverWindow()
window.set_title('Floaters')
window.connect('delete-event', gtk.main_quit)
window.set_default_size(1024, 768)
window.realize()

window.modify_bg(gtk.STATE_NORMAL, gdk.color_parse("black"))

label = gtk.Label()
label.set_markup("<span foreground=\"white\"><big>Hello, world</big></span>")
label.show()
window.add(label)
window.show()

gtk.main()

As I briefly note in the code, the secret sauce of this code is to get the $XSCREENSAVER_WINDOW id from xscreensaver when xscreensaver calls your program. This code does that.

(The code from the two URLs I linked to don’t work on their own; this code is a combination of those two. There may be excessive/redundant code in there; I haven’t figured that out yet.)

Make it executable like this:

$ chmod +x /home/al/hello.py

Then add this line to the end of the “programs” section in your .xscreensaver file, which is in your home directory (assuming that you’re already using xscreensaver):

/home/al/hello.py \n\

To be clear, the last ten lines or so in the “programs” section of my .xscreensaver file look like this:

-                               lcdscrub -root                              \n\
- GL:                           photopile -root                             \n\
- GL:                           skytentacles -root                          \n\
- GL:                           rubikblocks -root                           \n\
- GL:                           companioncube -root                         \n\
- GL:                           hilbert -root                               \n\
- GL:                           tronbit -root                               \n\
                                unicode -root                               \n\
                                noflipqlo -ampm -root                       \n\
                                xdaliclock -builtin2 -fullscreen -root      \n\
                                /home/al/hello.py                           \n\

Now restart xscreensaver like this:

$ pkill xscreensaver && xscreensaver -nosplash &

Then go into the screensaver preferences, chose this screensaver from the list, click Preview, and you should see this screensaver show up.

If it doesn’t show up, you may need to install things like Python and GTK libraries. (Sorry, I don’t remember what’s installed by default, and what I’ve installed with apt-get.) Hopefully the errors will show up on your screen and you can debug them.

The xscreensaver configuration

I got the xscreensaver configuration idea from this url, particularly this section of the docs:

To play MP4, MPEG, QuickTime, FLV and AVI videos:

Install mpv and add something like the following to the `programs' preference 
in your .xscreensaver file:

   "My Movie"  mpv --really-quiet --no-audio --fs --loop=inf  \
                   --no-stop-screensaver                      \
                   --wid=$XSCREENSAVER_WINDOW                 \
                   $HOME/movies/poop.mp4                    \n\

Duplicate those lines for each other movie you would like to play.

It's also possible to encapsulate the above into a single shell script which 
would pick a movie file at random from a directory and play that. 
Several people have written such things; Google it. 

To play SWF Flash animations:

Macromedia's stand-alone Flash player has xscreensaver support, as of version 6.0.79 
and later. To use it, put a line like this in the `programs' preference in your 
.xscreensaver file:

   "My Flash"   gflashplayer -root $HOME/movies/my_flash.swf  \n\

To play anything else:

   Beats me. 

xscreensaver and Python

As I mentioned, this isn’t exactly a screensaver, but it is a start. If you know Python and Gtk better than I do, you can take off with this and create your own screensaver. As for me, I have to dig into some Python and Gtk docs to see how to do what I want to do ...