Writing a Desktop Environment with Python


So, writing a desktop environment is a difficult task to say the least.

We need so many components and it seems impossible, and working alone it kind of is. Still, even though it might sound surprising – it IS feasible to make a nice little prototype of desktop environment alone, which is what I did.

We’re gonna focus on linux DE.

Now it’s in no way a standard Desktop Environment but it is another proof of how slowly but surely we abstract the computer and our frameworks become more and more solid. I’d like to see at as a way to continue the TinyWM tradition.

To see the final product you can check:  Github

Yeah, it’s not the most impressive stuff but it gets the job done and it’s still in prototype at this stage so we don’t know how good it’ll end up but I think it’s good enough for me to share some knowledge as the internet lacked the info and made some barriers for python users.

So… how do you write it in python? Wasn’t it mostly C/C++ code?

First of all, because of WMs like TinyWM the path is pretty clear now – python has imported Xlib and replaced the C structs and functions with classes and methods instead which is comfortable but unfortunately lacking a bit in documentation.

My goto docs are: the pyxlib docs

So indeed we’re seeing an even nicer level of abstraction. We need not bother ourselves with sockets and the X11 protocol directly, we can use a wrapper that handles that stuff. You can consider using XCB as well but I liked the Xlib resources and import to python.

We can use Gtk to make our extra apps like our launcher.

Making the Window Manager

To make the window manager all we need to do is follow the paths of some already nice prototypes like TinyWM. It provides it’s extremely small python code in that same page. You can run it just like that.

Taking it a step further

Making stuff like resize is easy, the one thing that is really lacking in guidance is the stacking. So we need to implement a click to raise mechanism.

To make it there are two steps:

First, we bind our left click to the window:

# grab_window_events is method inside our even manager/handler

FOCUS_BUTTON = 1 # left click

def grab_window_events(self, window):

window.grab_button(FOCUS_BUTTON, 0, True,

So now we grabbed the FOCUS_BUTTON which is the left click (button code 1), but wait this means that any click events that we intercept won’t be consumed at the target window. We’ve disabled the mouse click for the target window.

To fix it, note that this time we use Xlib.X.GrabModeSync. This should be the only time but it’s important as we’re going to see.

Then, we handle the grabbing:

def handle_mouse_press(self, event):

if event.detail == FOCUS_BUTTON:
# focus: raise window
self.wm.display.allow_events(Xlib.X.ReplayPointer, event.time)

handle_mouse_press is the method that handles the event where

event.type == Xlib.X.ButtonPress.

And now the important part:

self.wm.display.allow_events(Xlib.X.ReplayPointer, event.time)

This is why we used GrabModeSync. We select our display and allow ReplayPointer via XAllowEvents .

This allows us to let the events re-enter the event queue while ignoring synchronous button grabs. Which means that we grab the event and then re-send it while ungrabbed. This allows us to raise the window while still having the mouse clicks work normally.

Note that the python usage of this was nowhere to be found on the net and also the guidance to use XAllowEvents.

Hope this helps a soul out there.

Creating a Launcher

To create a launcher we can use psutil to search for open processes. It’s cross platform which is great. I used psutil.pids() to probe the processes.

To create any graphics I used Gtk 3 (which is what you should use). You can make it in python but be sure to use gi.repository instead of the old gtk package.

Here’s how to import:

from gi import require_version
require_version(‘Gtk’, ‘3.0’)
# import gtk
from gi.repository import Gtk, Gdk, GdkPixbuf, GLib

Here I also imported Gdk, GdkPixbuf (which is an object for stretching images to a size of your liking) and GLib (which is used for multithreading).

I didn’t use it yet but to get notified about closing/opening we can use pyinotify on “/proc” to get the events. To make transparent backgrounds you need to use Cairo and connect our window to a “draw” event:

window.connect(“draw”, self.area_draw)

and some more preparations:

window.visual = window.screen.get_rgba_visual()
if window.visual != None and window.screen.is_composited():
print “Advance graphics aren’t supported!”

we can use the next draw function:

def area_draw(self, widget, cr):

cr.set_source_rgba(.0, .0, .0, .0)

Here we used Cairo graphics library. Simply import cairo after installing the library.

To make clickable images we can use Gtk.EventBox and add the Gtk.Image. Finally, we connect it to the button_press_event and we’re done. In my launcher I made it so that a click would terminate the app for example, but you can make it as complex as you want of course.

Now for the gtk multithreading. The easiest way is to seperate the gui updates from other updates so that we can use:


GLib.timeout_add_seconds(1, update_gui)


to update the gui each second for example. We start normally via Gtk.main() while having a callback to update the gui. Neat.


Well, that was a lot to cover but I just went for the highlights that are hard to find online. Now you too can create a shitty desktop envirnoment but it’s a step forward.

Obviously this is nothing compared to a full Desktop Environment, which has so much more components (read more here) but we finally have a “TinyDE”. And it’s entirely in python which is extra convenient.

Until next time =)


2 thoughts on “Writing a Desktop Environment with Python

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s