A long time ago in a galaxy far away there was a non-KDE application that supported activities. That is, reporting opened documents to the activity manager (KAMD).

That code was essentially left for dead. Mainly due to some missing features and changes in the activity manager’s API. It was the time to play Dr Frankenstein and to pick up the pieces to revive this beast.

The Share·Like·Connect problem

While the normal part (statistics collector) of KAMD can work well with an application only reporting which document was opened (like the previous version of the vim plugin did), SLC needs one more info - the window id.

At the time, there was no way to get a window id out of GUI Vim if you’re under X11. It was introduced in later versions, but might not be present on your system. So, in those cases, there needed to be a different approach of getting the valuable data.

Enter the python scripting and Xlib

Here it gets painful. While writing vim scripts in python is rather pleasant, and my usual choice, playing around with Xlib is very irritating. The problem at hand, essentially boils down to getting the process ID (os.getpid) and finding a window that has _NET_WM_PID set to that value. Sounds trivial, yet it needs >50 lines of readable code due to the need to traverse the window tree hierarchy:

import os, sys, Xlib
from Xlib import X, display, Xatom

# Doing dirty things to stop Xlib from
# writing to stdout
# Xlib.display.Display()
#     Xlib.protocol.request.QueryExtension
old_stdout = sys.stdout
sys.stdout = open("/dev/null", "w")
display    = Xlib.display.Display()
screen     = display.screen()
sys.stdout.close()
sys.stdout = old_stdout

def _processWindow(win, atom, processId, level = 0):
    result = set()

    response = win.get_full_property(atom, Xatom.CARDINAL);

    found = False

    # Testing whether the response was valid
    # and whether we found a proper process id
    if response != None:
        for pid in response.value:
            if pid == processId:
                result.add(win.id)
                found = True

    # If we have found the window, we don't
    # search its children
    if not found:
        for child in win.query_tree().children:
            result |= _processWindow(child, atom, processId, level + 1)

    return result

# Gets the window IDs that belong to the specified process
def getWindowIds(processId):

    root = screen.root;
    tree = root.query_tree();
    wins = tree.children;
    atom = display.intern_atom("_NET_WM_PID", 1);

    # recursively searches the window tree
    # for the one that has a desired pid
    result = set()

    for win in wins:
        result |= _processWindow(win, atom, processId)

    return result

# Gets the window IDs that belong to the current process
def getWindowIdsForCurrentProcess():
    return getWindowIds(os.getpid())

Guessing it can be shorter, maybe Xlib has some magic methods to do this, maybe they are exported to the Python binding, but I haven’t found them.

No GVim, just Konsole and Vim?

If you don’t use GVim, but like to start normal Vim as a part of your Konsole session, the former approach doesn’t work. The window doesn’t belong to Vim’s process. Now, you’d say this will be a more difficult thing to solve than the above. But, alas, Konsole (and I guess a few other terminal emulators as well) is quite nice and provides an environment variable called WINDOWID.

Strange, right? Getting a window id for a non-gui Vim is easier than for GVim.

Vim and KAMD
Vim and KAMD

The only downside in this case is that Vim can’t tell when you switch to another tab in Konsole, so SLC still thinks that the document from the previous tab is shown. But, it is a rather minor nuance to really pay attention to.

The code and Emacs

The code is and will be in kde:activities-support.

It would be rather nice if somebody ported this to Emacs, so that we can cover two most popular geek editors in the world.

Others…

There is one more plugin coming. For one of the most popular applications on all platforms. But I’m not going to reveal it just yet.

I’ll start patching applications from the KDE world once we release 4.9, and get out of the feature-freeze period.