Screensaver for KDE using Python and Pygame

At Mendeley we have already done two hackdays. That’s a quite interesting event: during a period of time (the first one was on Friday until late night or overnight, second one Saturday and Sunday) we do whatever we want to do, it only needs to be a bit related to Mendeley.

On the last hack weekend I did a screensaver using Python, which one is integrated with KDE. Probably with some small changes it would work in other systems.

In this entry I’ll cover the basic of the screensavers in KDE (how to integrate the new screensaver in KDE), and how KDE interacts with the screensaver. You should know a little bit of Python and Pygame, but if you don’t know you can still understand the code and do the same using other systems.

The code is in Git. Or if you want the quick example look here.

How to add a new screensaver in the screensavers list from the system

In KDE, in Settings, Display icon, screensavers: there is a list of screensavers (if you don’t have any you can install the packages kde-window-manager and kscreensaver-xsavers).

If you want to add a new screensaver there, you can create a file in the directory /usr/share/kde4/services/ScreenSavers , my new file is named “Mendeley.desktop” where you can follow the same structure than other entries there. My one looks like:

[Desktop Entry]
Exec=mendeley.kss
Icon=preferences-desktop-screensaver
Type=Service
X-KDE-ServiceTypes=ScreenSaver
Actions=Setup;InWindow;Root;
# X-KDE-Category=Miscellaneous
Name=Mendeley
Name[ca]=Mendeley

[Desktop Action Setup]
Exec=mendeley.kss -setup
Name=Setup...
Name[ca]=Arranjament...
Icon=preferences-desktop-screensaver

[Desktop Action InWindow]
Exec=mendeley.kss -window-id %w
Name=Display in Specified Window
Name[ca]=Mostra a la finestra especificada
NoDisplay=true

[Desktop Action Root]
Exec=mendeley.kss -root
Name=Display in Root Window
Name[ca]=Mostra a la finestra arrel
NoDisplay=true

See the different sections: Desktop Entry is a general one, defining which actions that particular screensaver can do (in the example: all of them: setup, preview -InWindow- and the real screensaver thing -root).

In each action it defines which parameters will be passed to the binary. They can be changed, I’ve used the same as the other screensavers.

In the above case, it says that when Mendeley screensaver is called in preview mode (or test mode) it will pass the parameter -window-id and then the window id. In execution mode it will pass only -root parameter, and in setup mode -setup.

In our case, mendeley.kss is just a Python file (the extension .kss is to use the same extensions as other screensavers, could be whatever we want).

The mendeley.kss should be in some directory in the path of the current user (else, the full path could be also used).

Preview / test mode: window-id parameter

Pygame, by default, uses SDL as an output. And SDL reads an environment variable (SDL_WINDOWID), if the variable exists, it uses that window to paint the graphics. Else SDL creates a new window, as it usually happens.

The basic code to handle the test and the preview mode would be like:


#!/usr/bin/python

import os
import pygame

if sys.argv[1] == “-window-id”:
    os.environ[‘SDL_WINDOWID’] = sys.argv[2]

    pygame.init()
self.window = pygame.display.set_mode((0,0)) # auto-resize

From here on we would just write standard Python using Pygame.

Root window: the real case

In case that the screensaver executable receives -root parameter, we need to search the root window of the current desktop.

As a first approach (that doesn’t work here), this could be found using xprop -root and then getting the “window id” from the output of the command. But we will find that when KDE is trying to use the screensaver, nothing will appear on the screen (as a note, here is where I spent quite a long time to understand what was happening…).

What we need to do is search for the virtual root window. This is a window that we need to use. This virtual root window is a window child of the root window and that one of the properties is __SWM_ROOT. Code to do this can be found in git.

What the code does is to get all the children of the real root window, iterate over them and search which one contains __SWM_VROOT. Then returns the window id of the window that contains __SWM_VROOT.

The first version of the screensaver used a small C executable with some code borrowed from xscreensaver project that was doing the same using C libraries (libx11). I thought that it would be more clear, Pythonic and easier to understand if I would implement it using xwininfo and xprop. If somebody knows a way to get the same result using just Python (without external calls) feel free to comment or mail me.

The code in mendeley.kss (which is the main Python file) to get the Virtual Root Window is like:

if sys.argv[1] == "-root":
    windowid = virtualroot.getVirtualRootWindow()
    os.environ['SDL_WINDOWID'] = windowid

Instead of using the window-id that KDE provides us: searches for the windowid of the virtual root window and uses it.

Configuration

Maybe we would like to have a setup dialog for our new shiny screensaver. In that case (already specified in the .desktop file) KDE will launch the executable with the option -setup. I did something very simple as a proof of concept: I just launch a Qt dialog where the user can change some values, and the values are saved in a .py file. The main screensaver file just imports that .py file. Don’t do the same into the wild!

The code to do this (including the Qt code, etc.) can be found in the file mendeleyScreensaverSetup.py.

Exiting

When the user moves the mouse (or after writing the password): the KDE screensaver system will send a SIGTERM to the screensaver. In the handle_input of the screensaver code we can do:


def handle_input(self,events):
for event in events:
# SIGTERM is handled by SDL and received a QUIT event
if event.type == QUIT:
    sys.exit(0)

All together now…

All the code for the Mendeley screensaver can be found in git.

I’ve done a reduced version without mainly any logic (only a text is displayed) so you could create your own screensaver from there on. See the code.

In my system, when the screensaver is printing something to the standard output, this is saved in the file ~/.xsession-errors .

What about other desktop systems? (or operating systems?)

Screensavers in Gnome? Maybe just xscreensaver? Or even on Mac OS X? Microsoft Windows? I haven’t done it, but as far as I’ve seen in Google the principles are quite similar. Good luck, comment here if you wish. And perhaps I’ll do some other articles about how to do screensavers in the other systems.

Here I found some information regarding Microsoft Windows (I haven’t tested): http://archives.seul.org/pygame/users/Mar-2003/msg00162.html

Conclusion

Doing screensaver using Python and Pygame is easy (when you have the basic) and can be very fun. And leave a quite big potential there: would you like your screensaver to do some network connections and show webpages? Or perhaps take photos of your desk? Or maybe a clock with how long you haven’t been there? Or show randomly your last git messages? everything can be done, it’s just Python code.

Also, thanks to Mendeley to organise the hackdays / hackweekends. It’s fun, you learn, you code for fun… without thinking too much about maintenance or other constraints. We did a blog post with some screenshots of some hacks.

Also, if you have any idea for some hackday: tell me. It’s usually more fun to do something that could potentially be useful (like an amazing screensaver 🙂 ) than something not so terribly useful.

If you are curious what the Mendeley screensaver does: it connects to Mendeley using the Open-API, fetches some stats, shows on the screen using a black background and green fixed-width font, with a scrolling up. It also shows a random paper from your library and a random paper from the Mendeley catalog.

1 comment to Screensaver for KDE using Python and Pygame

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>